<commit_before>package convert

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"sort"
	"strconv"
	"time"
)

// Convert string to specify type.
type StrTo string

func (f StrTo) Exist() bool {
	return string(f) != string(0x1E)
}

func (f StrTo) Uint8() (uint8, error) {
	v, err := strconv.ParseUint(f.String(), 10, 8)
	return uint8(v), err
}

func (f StrTo) Int() (int, error) {
	v, err := strconv.ParseInt(f.String(), 10, 0)
	return int(v), err
}

func (f StrTo) Int64() (int64, error) {
	v, err := strconv.ParseInt(f.String(), 10, 64)
	return int64(v), err
}

func (f StrTo) MustUint8() uint8 {
	v, _ := f.Uint8()
	return v
}

func (f StrTo) MustInt() int {
	v, _ := f.Int()
	return v
}

func (f StrTo) MustInt64() int64 {
	v, _ := f.Int64()
	return v
}

func (f StrTo) String() string {
	if f.Exist() {
		return string(f)
	}
	return ""
}

func (f StrTo) Time(format ...string) (time.Time, error) {
	var layout = "2006-01-02"
	if len(format) != 0 {
		layout = format[0]
	}
	return time.Parse(layout, string(f))
}

func (f StrTo) MustTime(format ...string) time.Time {
	t, _ := f.Time(format...)
	return t
}

func (f StrTo) Md5() string {
	m := md5.New()
	m.Write([]byte(string(f)))
	return hex.EncodeToString(m.Sum(nil))
}

// Convert any type to string.
func ToStr(value interface{}, args ...int) (s string) {
	switch v := value.(type) {
	case bool:
		s = strconv.FormatBool(v)
	case float32:
		s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32))
	case float64:
		s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64))
	case int:
		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
	case int8:
		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
	case int16:
		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
	case int32:
		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
	case int64:
		s = strconv.FormatInt(v, argInt(args).Get(0, 10))
	case uint:
		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
	case uint8:
		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
	case uint16:
		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
	case uint32:
		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
	case uint64:
		s = strconv.FormatUint(v, argInt(args).Get(0, 10))
	case string:
		s = v
	case []byte:
		s = string(v)
	default:
		s = fmt.Sprintf("%v", v)
	}
	return s
}

type argInt []int

func (a argInt) Get(i int, args ...int) (r int) {
	if i >= 0 && i < len(a) {
		r = a[i]
	} else if len(args) > 0 {
		r = args[0]
	}
	return
}

// HexStr2int converts hex format string to decimal number.
func HexStr2int(hexStr string) (int, error) {
	num := 0
	length := len(hexStr)
	for i := 0; i < length; i++ {
		char := hexStr[length-i-1]
		factor := -1

		switch {
		case char >= '0' && char <= '9':
			factor = int(char) - '0'
		case char >= 'a' && char <= 'f':
			factor = int(char) - 'a' + 10
		default:
			return -1, fmt.Errorf("invalid hex: %s", string(char))
		}

		num += factor * PowInt(16, i)
	}
	return num, nil
}

// Int2HexStr converts decimal number to hex format string.
func Int2HexStr(num int) (hex string) {
	if num == 0 {
		return "0"
	}

	for num > 0 {
		r := num % 16

		c := "?"
		if r >= 0 && r <= 9 {
			c = string(r + '0')
		} else {
			c = string(r + 'a' - 10)
		}
		hex = c + hex
		num = num / 16
	}
	return hex
}

// PowInt is int type of math.Pow function.
func PowInt(x int, y int) int {
	if y <= 0 {
		return 1
	} else {
		if y%2 == 0 {
			sqrt := PowInt(x, y/2)
			return sqrt * sqrt
		} else {
			return PowInt(x, y-1) * x
		}
	}
}

func RuneAccumulation(str string) rune {
	var val rune
	for _, s := range str {
		val += rune(s)
	}
	return val
}

func StringSort(str1, str2 string) (small, big string) {
	s := []string{str1, str2}
	sort.Strings(s)
	return s[0], s[1]
}

func StringSortByRune(str1, str2 string) (small, big string) {
	if RuneAccumulation(str1) > RuneAccumulation(str2) {
		return str2, str1
	} else {
		return str1, str2
	}
}
<commit_msg>// String format conversion time // format must go time source has been package format<commit_after>package convert

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"sort"
	"strconv"
	"time"
)

// Convert string to specify type.
type StrTo string

func (f StrTo) Exist() bool {
	return string(f) != string(0x1E)
}

func (f StrTo) Uint8() (uint8, error) {
	v, err := strconv.ParseUint(f.String(), 10, 8)
	return uint8(v), err
}

func (f StrTo) Int() (int, error) {
	v, err := strconv.ParseInt(f.String(), 10, 0)
	return int(v), err
}

func (f StrTo) Int64() (int64, error) {
	v, err := strconv.ParseInt(f.String(), 10, 64)
	return int64(v), err
}

func (f StrTo) MustUint8() uint8 {
	v, _ := f.Uint8()
	return v
}

func (f StrTo) MustInt() int {
	v, _ := f.Int()
	return v
}

func (f StrTo) MustInt64() int64 {
	v, _ := f.Int64()
	return v
}

func (f StrTo) String() string {
	if f.Exist() {
		return string(f)
	}
	return ""
}

// String format conversion time
// format must go time source has been package format
func (f StrTo) Time(format ...string) (time.Time, error) {
	var layout = "2006-01-02"
	if len(format) != 0 {
		layout = format[0]
	}
	return time.Parse(layout, string(f))
}

func (f StrTo) MustTime(format ...string) time.Time {
	t, _ := f.Time(format...)
	return t
}

func (f StrTo) Md5() string {
	m := md5.New()
	m.Write([]byte(string(f)))
	return hex.EncodeToString(m.Sum(nil))
}

// Convert any type to string.
func ToStr(value interface{}, args ...int) (s string) {
	switch v := value.(type) {
	case bool:
		s = strconv.FormatBool(v)
	case float32:
		s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32))
	case float64:
		s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64))
	case int:
		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
	case int8:
		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
	case int16:
		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
	case int32:
		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
	case int64:
		s = strconv.FormatInt(v, argInt(args).Get(0, 10))
	case uint:
		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
	case uint8:
		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
	case uint16:
		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
	case uint32:
		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
	case uint64:
		s = strconv.FormatUint(v, argInt(args).Get(0, 10))
	case string:
		s = v
	case []byte:
		s = string(v)
	default:
		s = fmt.Sprintf("%v", v)
	}
	return s
}

type argInt []int

func (a argInt) Get(i int, args ...int) (r int) {
	if i >= 0 && i < len(a) {
		r = a[i]
	} else if len(args) > 0 {
		r = args[0]
	}
	return
}

// HexStr2int converts hex format string to decimal number.
func HexStr2int(hexStr string) (int, error) {
	num := 0
	length := len(hexStr)
	for i := 0; i < length; i++ {
		char := hexStr[length-i-1]
		factor := -1

		switch {
		case char >= '0' && char <= '9':
			factor = int(char) - '0'
		case char >= 'a' && char <= 'f':
			factor = int(char) - 'a' + 10
		default:
			return -1, fmt.Errorf("invalid hex: %s", string(char))
		}

		num += factor * PowInt(16, i)
	}
	return num, nil
}

// Int2HexStr converts decimal number to hex format string.
func Int2HexStr(num int) (hex string) {
	if num == 0 {
		return "0"
	}

	for num > 0 {
		r := num % 16

		c := "?"
		if r >= 0 && r <= 9 {
			c = string(r + '0')
		} else {
			c = string(r + 'a' - 10)
		}
		hex = c + hex
		num = num / 16
	}
	return hex
}

// PowInt is int type of math.Pow function.
func PowInt(x int, y int) int {
	if y <= 0 {
		return 1
	} else {
		if y%2 == 0 {
			sqrt := PowInt(x, y/2)
			return sqrt * sqrt
		} else {
			return PowInt(x, y-1) * x
		}
	}
}

func RuneAccumulation(str string) rune {
	var val rune
	for _, s := range str {
		val += rune(s)
	}
	return val
}

func StringSort(str1, str2 string) (small, big string) {
	s := []string{str1, str2}
	sort.Strings(s)
	return s[0], s[1]
}

func StringSortByRune(str1, str2 string) (small, big string) {
	if RuneAccumulation(str1) > RuneAccumulation(str2) {
		return str2, str1
	} else {
		return str1, str2
	}
}
<|endoftext|>


<commit_before>package views

import (
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"

	"github.com/jclebreton/opensirene/api/models"
)

// GetSiren is in charge of querying the database to get the specific records
// for a single Siren given in the query
func (v *ViewsContext) GetSiren(c *gin.Context) {
	var err error
	var es models.Enterprises
	limit := -1
	offset := -1
	siren := c.Param("id")
	lim := c.DefaultQuery("limit", "")
	off := c.DefaultQuery("offset", "")

	if lim != "" {
		if limit, err = strconv.Atoi(lim); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "'limit' query parameter isn't an integer"})
			return
		}
	}
	if off != "" {
		if offset, err = strconv.Atoi(off); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "'offset' query parameter isn't an integer"})
			return
		}
	}

	res := v.GormClient.Limit(limit).Offset(offset).Order("nic ASC").Find(&es, models.Enterprise{Siren: siren})
	if res.RecordNotFound() || len(es) == 0 {
		c.Status(http.StatusNotFound)
		return
	}
	if res.Error != nil {
		c.Status(http.StatusInternalServerError)
		return
	}

	c.JSON(http.StatusOK, es)
}

// GetSiret is in charge of querying the database to get the specific enterprise
// record for a single Siret given in the query
func (v *ViewsContext) GetSiret(c *gin.Context) {
	var e models.Enterprise

	siret := c.Param("id")
	if len(siret) != 14 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "not a valid siret: must be 14-digit number"})
		return
	}

	if v.GormClient.Find(&e, models.Enterprise{Siren: siret[0:9], Nic: siret[9:14]}).RecordNotFound() {
		c.Status(http.StatusNotFound)
		return
	}

	e.Siret = e.Siren + e.Nic

	c.JSON(http.StatusOK, e)
}
<commit_msg>Fix siret for siren endpoint<commit_after>package views

import (
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"

	"github.com/jclebreton/opensirene/api/models"
)

// GetSiren is in charge of querying the database to get the specific records
// for a single Siren given in the query
func (v *ViewsContext) GetSiren(c *gin.Context) {
	var err error
	var es models.Enterprises
	limit := -1
	offset := -1
	siren := c.Param("id")
	lim := c.DefaultQuery("limit", "")
	off := c.DefaultQuery("offset", "")

	if lim != "" {
		if limit, err = strconv.Atoi(lim); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "'limit' query parameter isn't an integer"})
			return
		}
	}
	if off != "" {
		if offset, err = strconv.Atoi(off); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "'offset' query parameter isn't an integer"})
			return
		}
	}

	res := v.GormClient.Limit(limit).Offset(offset).Order("nic ASC").Find(&es, models.Enterprise{Siren: siren})
	if res.RecordNotFound() || len(es) == 0 {
		c.Status(http.StatusNotFound)
		return
	}
	if res.Error != nil {
		c.Status(http.StatusInternalServerError)
		return
	}

	for k, e := range es {
		es[k].Siret = e.Siren + e.Nic
	}

	c.JSON(http.StatusOK, es)
}

// GetSiret is in charge of querying the database to get the specific enterprise
// record for a single Siret given in the query
func (v *ViewsContext) GetSiret(c *gin.Context) {
	var e models.Enterprise

	siret := c.Param("id")
	if len(siret) != 14 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "not a valid siret: must be 14-digit number"})
		return
	}

	if v.GormClient.Find(&e, models.Enterprise{Siren: siret[0:9], Nic: siret[9:14]}).RecordNotFound() {
		c.Status(http.StatusNotFound)
		return
	}

	e.Siret = e.Siren + e.Nic

	c.JSON(http.StatusOK, e)
}
<|endoftext|>


<commit_before>// Copyright 2018 The Fractal Team Authors
// This file is part of the fractal project.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package ftservice

import (
	"context"
	"math/big"

	"github.com/ethereum/go-ethereum/common/math"
	"github.com/fractalplatform/fractal/accountmanager"
	"github.com/fractalplatform/fractal/common"
	"github.com/fractalplatform/fractal/consensus"
	"github.com/fractalplatform/fractal/feemanager"
	"github.com/fractalplatform/fractal/ftservice/gasprice"
	"github.com/fractalplatform/fractal/p2p/enode"
	"github.com/fractalplatform/fractal/params"
	"github.com/fractalplatform/fractal/processor"
	"github.com/fractalplatform/fractal/processor/vm"
	"github.com/fractalplatform/fractal/rawdb"
	"github.com/fractalplatform/fractal/rpc"
	"github.com/fractalplatform/fractal/snapshot"
	"github.com/fractalplatform/fractal/state"
	"github.com/fractalplatform/fractal/txpool"
	"github.com/fractalplatform/fractal/types"
	"github.com/fractalplatform/fractal/utils/fdb"
)

// APIBackend implements ftserviceapi.Backend for full nodes
type APIBackend struct {
	ftservice *FtService
	gpo       *gasprice.Oracle
}

// ChainConfig returns the active chain configuration.
func (b *APIBackend) ChainConfig() *params.ChainConfig {
	return b.ftservice.chainConfig
}
func (b *APIBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
	return b.gpo.SuggestPrice(ctx)
}

func (b *APIBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
	number := rawdb.ReadHeaderNumber(b.ftservice.chainDb, hash)
	if number == nil {
		return nil, nil
	}
	receipts := rawdb.ReadReceipts(b.ftservice.chainDb, hash, *number)
	if receipts == nil {
		return nil, nil
	}
	logs := make([][]*types.Log, len(receipts))
	for i, receipt := range receipts {
		logs[i] = receipt.Logs
	}
	return logs, nil
}

func (b *APIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
	return b.ftservice.txPool.AddLocal(signedTx)
}

func (b *APIBackend) TxPool() *txpool.TxPool {
	return b.ftservice.TxPool()
}

func (b *APIBackend) ChainDb() fdb.Database {
	return b.ftservice.chainDb
}

func (b *APIBackend) CurrentBlock() *types.Block {
	return b.ftservice.blockchain.CurrentBlock()
}

func (b *APIBackend) GetBlock(ctx context.Context, hash common.Hash) (*types.Block, error) {
	return b.ftservice.blockchain.GetBlockByHash(hash), nil
}

func (b *APIBackend) GetReceipts(ctx context.Context, hash common.Hash) ([]*types.Receipt, error) {
	if number := rawdb.ReadHeaderNumber(b.ftservice.chainDb, hash); number != nil {
		return rawdb.ReadReceipts(b.ftservice.chainDb, hash, *number), nil
	}
	return nil, nil
}

func (b *APIBackend) GetDetailTxsLog(ctx context.Context, hash common.Hash) ([]*types.DetailTx, error) {
	if number := rawdb.ReadHeaderNumber(b.ftservice.chainDb, hash); number != nil {
		return rawdb.ReadDetailTxs(b.ftservice.chainDb, hash, *number), nil
	}
	return nil, nil
}

func (b *APIBackend) GetBlockDetailLog(ctx context.Context, blockNr rpc.BlockNumber) *types.BlockAndResult {
	hash := rawdb.ReadCanonicalHash(b.ftservice.chainDb, uint64(blockNr))
	if hash == (common.Hash{}) {
		return nil
	}
	receipts := rawdb.ReadReceipts(b.ftservice.chainDb, hash, uint64(blockNr))
	txDetails := rawdb.ReadDetailTxs(b.ftservice.chainDb, hash, uint64(blockNr))
	return &types.BlockAndResult{
		Receipts:  receipts,
		DetailTxs: txDetails,
	}
}

func (b *APIBackend) GetTxsByFilter(ctx context.Context, filterFn func(common.Name) bool, blockNr, lookbackNum uint64) []common.Hash {
	var lastnum uint64
	if lookbackNum > blockNr {
		lastnum = 0
	} else {
		lastnum = blockNr - lookbackNum
	}
	txHashs := make([]common.Hash, 0)
	for ublocknum := blockNr; ublocknum >= lastnum; ublocknum-- {
		hash := rawdb.ReadCanonicalHash(b.ftservice.chainDb, ublocknum)
		if hash == (common.Hash{}) {
			continue
		}

		blockBody := rawdb.ReadBody(b.ftservice.chainDb, hash, ublocknum)
		if blockBody == nil {
			continue
		}
		batchTxs := blockBody.Transactions

		for _, tx := range batchTxs {
			for _, act := range tx.GetActions() {
				if filterFn(act.Sender()) || filterFn(act.Recipient()) {
					txHashs = append(txHashs, tx.Hash())
					break
				}
			}
		}
	}

	return txHashs
}

func (b *APIBackend) GetDetailTxByFilter(ctx context.Context, filterFn func(common.Name) bool, blockNr, lookbackNum uint64) []*types.DetailTx {
	var lastnum uint64
	if lookbackNum > blockNr {
		lastnum = 0
	} else {
		lastnum = blockNr - lookbackNum
	}
	txdetails := make([]*types.DetailTx, 0)

	for ublocknum := blockNr; ublocknum >= lastnum; ublocknum-- {
		hash := rawdb.ReadCanonicalHash(b.ftservice.chainDb, ublocknum)
		if hash == (common.Hash{}) {
			continue
		}

		batchTxdetails := rawdb.ReadDetailTxs(b.ftservice.chainDb, hash, ublocknum)
		for _, txd := range batchTxdetails {
			newIntxs := make([]*types.DetailAction, 0)
			for _, intx := range txd.Actions {
				newInactions := make([]*types.InternalAction, 0)
				for _, inlog := range intx.InternalActions {
					if filterFn(inlog.Action.From) || filterFn(inlog.Action.To) {
						newInactions = append(newInactions, inlog)
					}
				}
				if len(newInactions) > 0 {
					newIntxs = append(newIntxs, &types.DetailAction{InternalActions: newInactions})
				}
			}

			if len(newIntxs) > 0 {
				txdetails = append(txdetails, &types.DetailTx{TxHash: txd.TxHash, Actions: newIntxs})
			}
		}
	}

	return txdetails
}

func (b *APIBackend) GetBadBlocks(ctx context.Context) ([]*types.Block, error) {
	return b.ftservice.blockchain.BadBlocks(), nil
}

func (b *APIBackend) GetTd(blockHash common.Hash) *big.Int {
	return b.ftservice.blockchain.GetTdByHash(blockHash)
}

func (b *APIBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) {
	if blockNr == rpc.LatestBlockNumber {
		return b.ftservice.blockchain.CurrentBlock().Header(), nil
	}
	return b.ftservice.blockchain.GetHeaderByNumber(uint64(blockNr)), nil
}

func (b *APIBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) {
	if blockNr == rpc.LatestBlockNumber {
		return b.ftservice.blockchain.CurrentBlock(), nil
	}
	return b.ftservice.blockchain.GetBlockByNumber(uint64(blockNr)), nil
}

func (b *APIBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
	header, err := b.HeaderByNumber(ctx, blockNr)
	if header == nil || err != nil {
		return nil, nil, err
	}
	stateDb, err := b.ftservice.blockchain.StateAt(b.ftservice.blockchain.CurrentBlock().Root())
	return stateDb, header, err
}

func (b *APIBackend) GetEVM(ctx context.Context, account *accountmanager.AccountManager, state *state.StateDB, from common.Name, to common.Name, assetID uint64, gasPrice *big.Int, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
	account.AddAccountBalanceByID(from, assetID, math.MaxBig256)
	vmError := func() error { return nil }

	evmcontext := &processor.EvmContext{
		ChainContext:  b.ftservice.BlockChain(),
		EngineContext: b.ftservice.Engine(),
	}

	context := processor.NewEVMContext(from, to, assetID, gasPrice, header, evmcontext, nil)
	return vm.NewEVM(context, account, state, b.ChainConfig(), vmCfg), vmError, nil
}

func (b *APIBackend) SetGasPrice(gasPrice *big.Int) bool {
	b.ftservice.SetGasPrice(gasPrice)
	return true
}

func (b *APIBackend) GetAccountManager() (*accountmanager.AccountManager, error) {
	sdb, err := b.ftservice.blockchain.State()
	if err != nil {
		return nil, err
	}
	return accountmanager.NewAccountManager(sdb)
}

//GetFeeManager get fee manager
func (b *APIBackend) GetFeeManager() (*feemanager.FeeManager, error) {
	sdb, err := b.ftservice.blockchain.State()
	if err != nil {
		return nil, err
	}
	acctm, err := accountmanager.NewAccountManager(sdb)
	if err != nil {
		return nil, err
	}

	fm := feemanager.NewFeeManager(sdb, acctm)
	return fm, nil
}

//GetFeeManagerByTime get fee manager
func (b *APIBackend) GetFeeManagerByTime(time uint64) (*feemanager.FeeManager, error) {
	sdb, err := b.ftservice.blockchain.State()
	if err != nil {
		return nil, err
	}

	snapshotManager := snapshot.NewSnapshotManager(sdb)
	state, err := snapshotManager.GetSnapshotState(time)
	if err != nil {
		return nil, err
	}

	acctm, err := accountmanager.NewAccountManager(state)
	if err != nil {
		return nil, err
	}

	fm := feemanager.NewFeeManager(state, acctm)
	return fm, nil
}

// AddPeer add a P2P peer
func (b *APIBackend) AddPeer(url string) error {
	node, err := enode.ParseV4(url)
	if err == nil {
		b.ftservice.p2pServer.AddPeer(node)
	}
	return err
}

// RemovePeer remove a P2P peer
func (b *APIBackend) RemovePeer(url string) error {
	node, err := enode.ParseV4(url)
	if err == nil {
		b.ftservice.p2pServer.RemovePeer(node)
	}
	return err
}

// AddTrustedPeer allows a remote node to always connect, even if slots are full
func (b *APIBackend) AddTrustedPeer(url string) error {
	node, err := enode.ParseV4(url)
	if err == nil {
		b.ftservice.p2pServer.AddTrustedPeer(node)
	}
	return err
}

// RemoveTrustedPeer removes a remote node from the trusted peer set, but it
// does not disconnect it automatically.
func (b *APIBackend) RemoveTrustedPeer(url string) error {
	node, err := enode.ParseV4(url)
	if err == nil {
		b.ftservice.p2pServer.RemoveTrustedPeer(node)
	}
	return err
}

// PeerCount returns the number of connected peers.
func (b *APIBackend) PeerCount() int {
	return b.ftservice.p2pServer.PeerCount()
}

// Peers returns all connected peers.
func (b *APIBackend) Peers() []string {
	ps := b.ftservice.p2pServer.Peers()
	peers := make([]string, len(ps))
	for i, peer := range ps {
		peers[i] = peer.Node().String()
	}
	return peers
}

// BadNodesCount returns the number of bad nodes.
func (b *APIBackend) BadNodesCount() int {
	return b.ftservice.p2pServer.BadNodesCount()
}

// BadNodes returns all bad nodes.
func (b *APIBackend) BadNodes() []string {
	nodes := b.ftservice.p2pServer.BadNodes()
	ns := make([]string, len(nodes))
	for i, node := range nodes {
		ns[i] = node.String()
	}
	return ns
}

// AddBadNode add a bad Node and would cause the node disconnected
func (b *APIBackend) AddBadNode(url string) error {
	node, err := enode.ParseV4(url)
	if err == nil {
		b.ftservice.p2pServer.AddBadNode(node, nil)
	}
	return err
}

// RemoveBadNode add a bad Node and would cause the node disconnected
func (b *APIBackend) RemoveBadNode(url string) error {
	node, err := enode.ParseV4(url)
	if err == nil {
		b.ftservice.p2pServer.RemoveBadNode(node)
	}
	return err
}

// SelfNode returns the local node's endpoint information.
func (b *APIBackend) SelfNode() string {
	return b.ftservice.p2pServer.Self().String()
}

func (b *APIBackend) Engine() consensus.IEngine {
	return b.ftservice.engine
}

//SetStatePruning set state pruning
func (b *APIBackend) SetStatePruning(enable bool) (bool, uint64) {
	return b.ftservice.blockchain.StatePruning(enable)
}

// APIs returns apis
func (b *APIBackend) APIs() []rpc.API {
	return b.ftservice.miner.APIs(b.ftservice.blockchain)
}
<commit_msg>Fix uint infinite loop (#388)<commit_after>// Copyright 2018 The Fractal Team Authors
// This file is part of the fractal project.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package ftservice

import (
	"context"
	"math/big"

	"github.com/ethereum/go-ethereum/common/math"
	"github.com/fractalplatform/fractal/accountmanager"
	"github.com/fractalplatform/fractal/common"
	"github.com/fractalplatform/fractal/consensus"
	"github.com/fractalplatform/fractal/feemanager"
	"github.com/fractalplatform/fractal/ftservice/gasprice"
	"github.com/fractalplatform/fractal/p2p/enode"
	"github.com/fractalplatform/fractal/params"
	"github.com/fractalplatform/fractal/processor"
	"github.com/fractalplatform/fractal/processor/vm"
	"github.com/fractalplatform/fractal/rawdb"
	"github.com/fractalplatform/fractal/rpc"
	"github.com/fractalplatform/fractal/snapshot"
	"github.com/fractalplatform/fractal/state"
	"github.com/fractalplatform/fractal/txpool"
	"github.com/fractalplatform/fractal/types"
	"github.com/fractalplatform/fractal/utils/fdb"
)

// APIBackend implements ftserviceapi.Backend for full nodes
type APIBackend struct {
	ftservice *FtService
	gpo       *gasprice.Oracle
}

// ChainConfig returns the active chain configuration.
func (b *APIBackend) ChainConfig() *params.ChainConfig {
	return b.ftservice.chainConfig
}
func (b *APIBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
	return b.gpo.SuggestPrice(ctx)
}

func (b *APIBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
	number := rawdb.ReadHeaderNumber(b.ftservice.chainDb, hash)
	if number == nil {
		return nil, nil
	}
	receipts := rawdb.ReadReceipts(b.ftservice.chainDb, hash, *number)
	if receipts == nil {
		return nil, nil
	}
	logs := make([][]*types.Log, len(receipts))
	for i, receipt := range receipts {
		logs[i] = receipt.Logs
	}
	return logs, nil
}

func (b *APIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
	return b.ftservice.txPool.AddLocal(signedTx)
}

func (b *APIBackend) TxPool() *txpool.TxPool {
	return b.ftservice.TxPool()
}

func (b *APIBackend) ChainDb() fdb.Database {
	return b.ftservice.chainDb
}

func (b *APIBackend) CurrentBlock() *types.Block {
	return b.ftservice.blockchain.CurrentBlock()
}

func (b *APIBackend) GetBlock(ctx context.Context, hash common.Hash) (*types.Block, error) {
	return b.ftservice.blockchain.GetBlockByHash(hash), nil
}

func (b *APIBackend) GetReceipts(ctx context.Context, hash common.Hash) ([]*types.Receipt, error) {
	if number := rawdb.ReadHeaderNumber(b.ftservice.chainDb, hash); number != nil {
		return rawdb.ReadReceipts(b.ftservice.chainDb, hash, *number), nil
	}
	return nil, nil
}

func (b *APIBackend) GetDetailTxsLog(ctx context.Context, hash common.Hash) ([]*types.DetailTx, error) {
	if number := rawdb.ReadHeaderNumber(b.ftservice.chainDb, hash); number != nil {
		return rawdb.ReadDetailTxs(b.ftservice.chainDb, hash, *number), nil
	}
	return nil, nil
}

func (b *APIBackend) GetBlockDetailLog(ctx context.Context, blockNr rpc.BlockNumber) *types.BlockAndResult {
	hash := rawdb.ReadCanonicalHash(b.ftservice.chainDb, uint64(blockNr))
	if hash == (common.Hash{}) {
		return nil
	}
	receipts := rawdb.ReadReceipts(b.ftservice.chainDb, hash, uint64(blockNr))
	txDetails := rawdb.ReadDetailTxs(b.ftservice.chainDb, hash, uint64(blockNr))
	return &types.BlockAndResult{
		Receipts:  receipts,
		DetailTxs: txDetails,
	}
}

func (b *APIBackend) GetTxsByFilter(ctx context.Context, filterFn func(common.Name) bool, blockNr, lookbackNum uint64) []common.Hash {
	var lastnum int64
	if lookbackNum > blockNr {
		lastnum = 0
	} else {
		lastnum = int64(blockNr - lookbackNum)
	}
	txHashs := make([]common.Hash, 0)
	for ublocknum := int64(blockNr); ublocknum >= lastnum; ublocknum-- {
		hash := rawdb.ReadCanonicalHash(b.ftservice.chainDb, uint64(ublocknum))
		if hash == (common.Hash{}) {
			continue
		}

		blockBody := rawdb.ReadBody(b.ftservice.chainDb, hash, uint64(ublocknum))
		if blockBody == nil {
			continue
		}
		batchTxs := blockBody.Transactions

		for _, tx := range batchTxs {
			for _, act := range tx.GetActions() {
				if filterFn(act.Sender()) || filterFn(act.Recipient()) {
					txHashs = append(txHashs, tx.Hash())
					break
				}
			}
		}
	}

	return txHashs
}

func (b *APIBackend) GetDetailTxByFilter(ctx context.Context, filterFn func(common.Name) bool, blockNr, lookbackNum uint64) []*types.DetailTx {
	var lastnum int64
	if lookbackNum > blockNr {
		lastnum = 0
	} else {
		lastnum = int64(blockNr - lookbackNum)
	}
	txdetails := make([]*types.DetailTx, 0)

	for ublocknum := int64(blockNr); ublocknum >= lastnum; ublocknum-- {
		hash := rawdb.ReadCanonicalHash(b.ftservice.chainDb, uint64(ublocknum))
		if hash == (common.Hash{}) {
			continue
		}

		batchTxdetails := rawdb.ReadDetailTxs(b.ftservice.chainDb, hash, uint64(ublocknum))
		for _, txd := range batchTxdetails {
			newIntxs := make([]*types.DetailAction, 0)
			for _, intx := range txd.Actions {
				newInactions := make([]*types.InternalAction, 0)
				for _, inlog := range intx.InternalActions {
					if filterFn(inlog.Action.From) || filterFn(inlog.Action.To) {
						newInactions = append(newInactions, inlog)
					}
				}
				if len(newInactions) > 0 {
					newIntxs = append(newIntxs, &types.DetailAction{InternalActions: newInactions})
				}
			}

			if len(newIntxs) > 0 {
				txdetails = append(txdetails, &types.DetailTx{TxHash: txd.TxHash, Actions: newIntxs})
			}
		}
	}

	return txdetails
}

func (b *APIBackend) GetBadBlocks(ctx context.Context) ([]*types.Block, error) {
	return b.ftservice.blockchain.BadBlocks(), nil
}

func (b *APIBackend) GetTd(blockHash common.Hash) *big.Int {
	return b.ftservice.blockchain.GetTdByHash(blockHash)
}

func (b *APIBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) {
	if blockNr == rpc.LatestBlockNumber {
		return b.ftservice.blockchain.CurrentBlock().Header(), nil
	}
	return b.ftservice.blockchain.GetHeaderByNumber(uint64(blockNr)), nil
}

func (b *APIBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) {
	if blockNr == rpc.LatestBlockNumber {
		return b.ftservice.blockchain.CurrentBlock(), nil
	}
	return b.ftservice.blockchain.GetBlockByNumber(uint64(blockNr)), nil
}

func (b *APIBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
	header, err := b.HeaderByNumber(ctx, blockNr)
	if header == nil || err != nil {
		return nil, nil, err
	}
	stateDb, err := b.ftservice.blockchain.StateAt(b.ftservice.blockchain.CurrentBlock().Root())
	return stateDb, header, err
}

func (b *APIBackend) GetEVM(ctx context.Context, account *accountmanager.AccountManager, state *state.StateDB, from common.Name, to common.Name, assetID uint64, gasPrice *big.Int, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
	account.AddAccountBalanceByID(from, assetID, math.MaxBig256)
	vmError := func() error { return nil }

	evmcontext := &processor.EvmContext{
		ChainContext:  b.ftservice.BlockChain(),
		EngineContext: b.ftservice.Engine(),
	}

	context := processor.NewEVMContext(from, to, assetID, gasPrice, header, evmcontext, nil)
	return vm.NewEVM(context, account, state, b.ChainConfig(), vmCfg), vmError, nil
}

func (b *APIBackend) SetGasPrice(gasPrice *big.Int) bool {
	b.ftservice.SetGasPrice(gasPrice)
	return true
}

func (b *APIBackend) GetAccountManager() (*accountmanager.AccountManager, error) {
	sdb, err := b.ftservice.blockchain.State()
	if err != nil {
		return nil, err
	}
	return accountmanager.NewAccountManager(sdb)
}

//GetFeeManager get fee manager
func (b *APIBackend) GetFeeManager() (*feemanager.FeeManager, error) {
	sdb, err := b.ftservice.blockchain.State()
	if err != nil {
		return nil, err
	}
	acctm, err := accountmanager.NewAccountManager(sdb)
	if err != nil {
		return nil, err
	}

	fm := feemanager.NewFeeManager(sdb, acctm)
	return fm, nil
}

//GetFeeManagerByTime get fee manager
func (b *APIBackend) GetFeeManagerByTime(time uint64) (*feemanager.FeeManager, error) {
	sdb, err := b.ftservice.blockchain.State()
	if err != nil {
		return nil, err
	}

	snapshotManager := snapshot.NewSnapshotManager(sdb)
	state, err := snapshotManager.GetSnapshotState(time)
	if err != nil {
		return nil, err
	}

	acctm, err := accountmanager.NewAccountManager(state)
	if err != nil {
		return nil, err
	}

	fm := feemanager.NewFeeManager(state, acctm)
	return fm, nil
}

// AddPeer add a P2P peer
func (b *APIBackend) AddPeer(url string) error {
	node, err := enode.ParseV4(url)
	if err == nil {
		b.ftservice.p2pServer.AddPeer(node)
	}
	return err
}

// RemovePeer remove a P2P peer
func (b *APIBackend) RemovePeer(url string) error {
	node, err := enode.ParseV4(url)
	if err == nil {
		b.ftservice.p2pServer.RemovePeer(node)
	}
	return err
}

// AddTrustedPeer allows a remote node to always connect, even if slots are full
func (b *APIBackend) AddTrustedPeer(url string) error {
	node, err := enode.ParseV4(url)
	if err == nil {
		b.ftservice.p2pServer.AddTrustedPeer(node)
	}
	return err
}

// RemoveTrustedPeer removes a remote node from the trusted peer set, but it
// does not disconnect it automatically.
func (b *APIBackend) RemoveTrustedPeer(url string) error {
	node, err := enode.ParseV4(url)
	if err == nil {
		b.ftservice.p2pServer.RemoveTrustedPeer(node)
	}
	return err
}

// PeerCount returns the number of connected peers.
func (b *APIBackend) PeerCount() int {
	return b.ftservice.p2pServer.PeerCount()
}

// Peers returns all connected peers.
func (b *APIBackend) Peers() []string {
	ps := b.ftservice.p2pServer.Peers()
	peers := make([]string, len(ps))
	for i, peer := range ps {
		peers[i] = peer.Node().String()
	}
	return peers
}

// BadNodesCount returns the number of bad nodes.
func (b *APIBackend) BadNodesCount() int {
	return b.ftservice.p2pServer.BadNodesCount()
}

// BadNodes returns all bad nodes.
func (b *APIBackend) BadNodes() []string {
	nodes := b.ftservice.p2pServer.BadNodes()
	ns := make([]string, len(nodes))
	for i, node := range nodes {
		ns[i] = node.String()
	}
	return ns
}

// AddBadNode add a bad Node and would cause the node disconnected
func (b *APIBackend) AddBadNode(url string) error {
	node, err := enode.ParseV4(url)
	if err == nil {
		b.ftservice.p2pServer.AddBadNode(node, nil)
	}
	return err
}

// RemoveBadNode add a bad Node and would cause the node disconnected
func (b *APIBackend) RemoveBadNode(url string) error {
	node, err := enode.ParseV4(url)
	if err == nil {
		b.ftservice.p2pServer.RemoveBadNode(node)
	}
	return err
}

// SelfNode returns the local node's endpoint information.
func (b *APIBackend) SelfNode() string {
	return b.ftservice.p2pServer.Self().String()
}

func (b *APIBackend) Engine() consensus.IEngine {
	return b.ftservice.engine
}

//SetStatePruning set state pruning
func (b *APIBackend) SetStatePruning(enable bool) (bool, uint64) {
	return b.ftservice.blockchain.StatePruning(enable)
}

// APIs returns apis
func (b *APIBackend) APIs() []rpc.API {
	return b.ftservice.miner.APIs(b.ftservice.blockchain)
}
<|endoftext|>


<commit_before>package test

import (
	"errors"
	"fmt"
	"math/rand"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"testing"
	"time"
)

const (
	retryCount = 2
	isParallel = true
)

type cmdFunc func() ([]byte, error)

// try is designed with command line executions in mind
func try(blockName string, t *t, f cmdFunc, maxTime time.Duration) {
	start := time.Now()
	var outp []byte
	var err error

	for {
		if t.failed {
			return
		}
		if time.Since(start) > maxTime {
			_, file, line, _ := runtime.Caller(1)
			fname := filepath.Base(file)
			if err != nil {
				t.Fatalf("%v:%v, %v (failed after %v with '%v'), output: '%v'", fname, line, blockName, time.Since(start), err, string(outp))
			}
			return
		}
		outp, err = f()
		if err == nil {
			return
		}
		time.Sleep(1000 * time.Millisecond)
	}
}

func once(blockName string, t *testing.T, f cmdFunc) {
	outp, err := f()
	if err != nil {
		t.Fatalf("%v with '%v', output: %v", blockName, err, string(outp))
	}
}

type server struct {
	cmd           *exec.Cmd
	t             *t
	envName       string
	containerName string
	opts          options
}

func getFrame(skipFrames int) runtime.Frame {
	// We need the frame at index skipFrames+2, since we never want runtime.Callers and getFrame
	targetFrameIndex := skipFrames + 2

	// Set size to targetFrameIndex+2 to ensure we have room for one more caller than we need
	programCounters := make([]uintptr, targetFrameIndex+2)
	n := runtime.Callers(0, programCounters)

	frame := runtime.Frame{Function: "unknown"}
	if n > 0 {
		frames := runtime.CallersFrames(programCounters[:n])
		for more, frameIndex := true, 0; more && frameIndex <= targetFrameIndex; frameIndex++ {
			var frameCandidate runtime.Frame
			frameCandidate, more = frames.Next()
			if frameIndex == targetFrameIndex {
				frame = frameCandidate
			}
		}
	}

	return frame
}

// taken from https://stackoverflow.com/questions/35212985/is-it-possible-get-information-about-caller-function-in-golang
// MyCaller returns the caller of the function that called it :)
func myCaller() string {
	// Skip GetCallerFunctionName and the function to get the caller of
	return getFrame(2).Function
}

type options struct {
	auth string // eg. jwt
}

func newServer(t *t, opts ...options) server {
	min := 8000
	max := 60000
	portnum := rand.Intn(max-min) + min
	fname := strings.Split(myCaller(), ".")[2]

	// kill container, ignore error because it might not exist,
	// we dont care about this that much
	exec.Command("docker", "kill", fname).CombinedOutput()
	exec.Command("docker", "rm", fname).CombinedOutput()

	// create env and set proxy address
	exec.Command("micro", "env", "add", fname, fmt.Sprintf("127.0.0.1:%v", portnum)).CombinedOutput()

	cmd := exec.Command("docker", "run", "--name", fname,
		fmt.Sprintf("-p=%v:8081", portnum), "micro", "server")
	if len(opts) == 1 && opts[0].auth == "jwt" {

		base64 := "base64 -w0"
		if runtime.GOOS == "darwin" {
			base64 = "base64 -b0"
		}
		priv := "cat /tmp/sshkey | " + base64
		privKey, err := exec.Command("bash", "-c", priv).Output()
		if err != nil {
			panic(string(privKey))
		}

		pub := "cat /tmp/sshkey.pub | " + base64
		pubKey, err := exec.Command("bash", "-c", pub).Output()
		if err != nil {
			panic(string(pubKey))
		}
		cmd = exec.Command("docker", "run", "--name", fname,
			fmt.Sprintf("-p=%v:8081", portnum),
			"-e", "MICRO_AUTH=jwt",
			"-e", "MICRO_AUTH_PRIVATE_KEY="+strings.Trim(string(privKey), "\n"),
			"-e", "MICRO_AUTH_PUBLIC_KEY="+strings.Trim(string(pubKey), "\n"),
			"micro", "server")
	}
	//fmt.Println("docker", "run", "--name", fname, fmt.Sprintf("-p=%v:8081", portnum), "micro", "server")
	opt := options{}
	if len(opts) > 0 {
		opt = opts[0]
	}
	return server{
		cmd:           cmd,
		t:             t,
		envName:       fname,
		containerName: fname,
		opts:          opt,
	}
}

func (s server) launch() {
	go func() {
		if err := s.cmd.Start(); err != nil {
			s.t.t.Fatal(err)
		}
	}()
	// @todo find a way to know everything is up and running
	if s.opts.auth == "jwt" {
		// when JWT is used we can't call `micro list services`
		// until we log in.
		time.Sleep(30 * time.Second)
	} else {
		try("Calling micro server", s.t, func() ([]byte, error) {
			outp, err := exec.Command("micro", s.envFlag(), "list", "services").CombinedOutput()
			if !strings.Contains(string(outp), "runtime") ||
				!strings.Contains(string(outp), "registry") ||
				!strings.Contains(string(outp), "api") ||
				!strings.Contains(string(outp), "broker") ||
				!strings.Contains(string(outp), "config") ||
				!strings.Contains(string(outp), "debug") ||
				!strings.Contains(string(outp), "proxy") ||
				!strings.Contains(string(outp), "auth") ||
				!strings.Contains(string(outp), "store") {
				return outp, errors.New("Not ready")
			}
			return outp, err
		}, 60*time.Second)
	}
	time.Sleep(5 * time.Second)
}

func (s server) close() {
	exec.Command("docker", "kill", s.containerName).CombinedOutput()
	if s.cmd.Process != nil {
		s.cmd.Process.Signal(syscall.SIGKILL)
	}
}

func (s server) envFlag() string {
	return fmt.Sprintf("-env=%v", s.envName)
}

type t struct {
	counter int
	failed  bool
	format  string
	values  []interface{}
	t       *testing.T
}

func (t *t) Fatal(values ...interface{}) {
	t.t.Log(values...)
	t.failed = true
	t.values = values
}

func (t *t) Log(values ...interface{}) {
	t.t.Log(values...)
}

func (t *t) Fatalf(format string, values ...interface{}) {
	t.t.Log(fmt.Sprintf(format, values...))
	t.failed = true
	t.values = values
	t.format = format
}

func (t *t) Parallel() {
	if t.counter == 0 && isParallel {
		t.t.Parallel()
	}
	t.counter++
}

func newT(te *testing.T) *t {
	return &t{t: te}
}

// trySuite is designed to retry a TestXX function
func trySuite(t *testing.T, f func(t *t), times int) {
	tee := newT(t)
	for i := 0; i < times; i++ {
		f(tee)
		if !tee.failed {
			return
		}
		if i != times-1 {
			tee.failed = false
		}
		time.Sleep(200 * time.Millisecond)
	}
	if tee.failed {
		if len(tee.format) > 0 {
			t.Fatalf(tee.format, tee.values...)
		} else {
			t.Fatal(tee.values...)
		}
	}
}
<commit_msg>test: increase retryCount<commit_after>package test

import (
	"errors"
	"fmt"
	"math/rand"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"testing"
	"time"
)

const (
	retryCount = 4
	isParallel = true
)

type cmdFunc func() ([]byte, error)

// try is designed with command line executions in mind
func try(blockName string, t *t, f cmdFunc, maxTime time.Duration) {
	start := time.Now()
	var outp []byte
	var err error

	for {
		if t.failed {
			return
		}
		if time.Since(start) > maxTime {
			_, file, line, _ := runtime.Caller(1)
			fname := filepath.Base(file)
			if err != nil {
				t.Fatalf("%v:%v, %v (failed after %v with '%v'), output: '%v'", fname, line, blockName, time.Since(start), err, string(outp))
			}
			return
		}
		outp, err = f()
		if err == nil {
			return
		}
		time.Sleep(1000 * time.Millisecond)
	}
}

func once(blockName string, t *testing.T, f cmdFunc) {
	outp, err := f()
	if err != nil {
		t.Fatalf("%v with '%v', output: %v", blockName, err, string(outp))
	}
}

type server struct {
	cmd           *exec.Cmd
	t             *t
	envName       string
	containerName string
	opts          options
}

func getFrame(skipFrames int) runtime.Frame {
	// We need the frame at index skipFrames+2, since we never want runtime.Callers and getFrame
	targetFrameIndex := skipFrames + 2

	// Set size to targetFrameIndex+2 to ensure we have room for one more caller than we need
	programCounters := make([]uintptr, targetFrameIndex+2)
	n := runtime.Callers(0, programCounters)

	frame := runtime.Frame{Function: "unknown"}
	if n > 0 {
		frames := runtime.CallersFrames(programCounters[:n])
		for more, frameIndex := true, 0; more && frameIndex <= targetFrameIndex; frameIndex++ {
			var frameCandidate runtime.Frame
			frameCandidate, more = frames.Next()
			if frameIndex == targetFrameIndex {
				frame = frameCandidate
			}
		}
	}

	return frame
}

// taken from https://stackoverflow.com/questions/35212985/is-it-possible-get-information-about-caller-function-in-golang
// MyCaller returns the caller of the function that called it :)
func myCaller() string {
	// Skip GetCallerFunctionName and the function to get the caller of
	return getFrame(2).Function
}

type options struct {
	auth string // eg. jwt
}

func newServer(t *t, opts ...options) server {
	min := 8000
	max := 60000
	portnum := rand.Intn(max-min) + min
	fname := strings.Split(myCaller(), ".")[2]

	// kill container, ignore error because it might not exist,
	// we dont care about this that much
	exec.Command("docker", "kill", fname).CombinedOutput()
	exec.Command("docker", "rm", fname).CombinedOutput()

	// create env and set proxy address
	exec.Command("micro", "env", "add", fname, fmt.Sprintf("127.0.0.1:%v", portnum)).CombinedOutput()

	cmd := exec.Command("docker", "run", "--name", fname,
		fmt.Sprintf("-p=%v:8081", portnum), "micro", "server")
	if len(opts) == 1 && opts[0].auth == "jwt" {

		base64 := "base64 -w0"
		if runtime.GOOS == "darwin" {
			base64 = "base64 -b0"
		}
		priv := "cat /tmp/sshkey | " + base64
		privKey, err := exec.Command("bash", "-c", priv).Output()
		if err != nil {
			panic(string(privKey))
		}

		pub := "cat /tmp/sshkey.pub | " + base64
		pubKey, err := exec.Command("bash", "-c", pub).Output()
		if err != nil {
			panic(string(pubKey))
		}
		cmd = exec.Command("docker", "run", "--name", fname,
			fmt.Sprintf("-p=%v:8081", portnum),
			"-e", "MICRO_AUTH=jwt",
			"-e", "MICRO_AUTH_PRIVATE_KEY="+strings.Trim(string(privKey), "\n"),
			"-e", "MICRO_AUTH_PUBLIC_KEY="+strings.Trim(string(pubKey), "\n"),
			"micro", "server")
	}
	//fmt.Println("docker", "run", "--name", fname, fmt.Sprintf("-p=%v:8081", portnum), "micro", "server")
	opt := options{}
	if len(opts) > 0 {
		opt = opts[0]
	}
	return server{
		cmd:           cmd,
		t:             t,
		envName:       fname,
		containerName: fname,
		opts:          opt,
	}
}

func (s server) launch() {
	go func() {
		if err := s.cmd.Start(); err != nil {
			s.t.t.Fatal(err)
		}
	}()
	// @todo find a way to know everything is up and running
	if s.opts.auth == "jwt" {
		// when JWT is used we can't call `micro list services`
		// until we log in.
		time.Sleep(30 * time.Second)
	} else {
		try("Calling micro server", s.t, func() ([]byte, error) {
			outp, err := exec.Command("micro", s.envFlag(), "list", "services").CombinedOutput()
			if !strings.Contains(string(outp), "runtime") ||
				!strings.Contains(string(outp), "registry") ||
				!strings.Contains(string(outp), "api") ||
				!strings.Contains(string(outp), "broker") ||
				!strings.Contains(string(outp), "config") ||
				!strings.Contains(string(outp), "debug") ||
				!strings.Contains(string(outp), "proxy") ||
				!strings.Contains(string(outp), "auth") ||
				!strings.Contains(string(outp), "store") {
				return outp, errors.New("Not ready")
			}
			return outp, err
		}, 60*time.Second)
	}
	time.Sleep(5 * time.Second)
}

func (s server) close() {
	exec.Command("docker", "kill", s.containerName).CombinedOutput()
	if s.cmd.Process != nil {
		s.cmd.Process.Signal(syscall.SIGKILL)
	}
}

func (s server) envFlag() string {
	return fmt.Sprintf("-env=%v", s.envName)
}

type t struct {
	counter int
	failed  bool
	format  string
	values  []interface{}
	t       *testing.T
}

func (t *t) Fatal(values ...interface{}) {
	t.t.Log(values...)
	t.failed = true
	t.values = values
}

func (t *t) Log(values ...interface{}) {
	t.t.Log(values...)
}

func (t *t) Fatalf(format string, values ...interface{}) {
	t.t.Log(fmt.Sprintf(format, values...))
	t.failed = true
	t.values = values
	t.format = format
}

func (t *t) Parallel() {
	if t.counter == 0 && isParallel {
		t.t.Parallel()
	}
	t.counter++
}

func newT(te *testing.T) *t {
	return &t{t: te}
}

// trySuite is designed to retry a TestXX function
func trySuite(t *testing.T, f func(t *t), times int) {
	tee := newT(t)
	for i := 0; i < times; i++ {
		f(tee)
		if !tee.failed {
			return
		}
		if i != times-1 {
			tee.failed = false
		}
		time.Sleep(200 * time.Millisecond)
	}
	if tee.failed {
		if len(tee.format) > 0 {
			t.Fatalf(tee.format, tee.values...)
		} else {
			t.Fatal(tee.values...)
		}
	}
}
<|endoftext|>


<commit_before>package test

import (
	"errors"
	"fmt"
	"math/rand"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"testing"
	"time"

	"github.com/micro/micro/v2/client/cli/namespace"
	"github.com/micro/micro/v2/client/cli/token"
)

const (
	retryCount = 2
	isParallel = true
)

type cmdFunc func() ([]byte, error)

// try is designed with command line executions in mind
func try(blockName string, t *t, f cmdFunc, maxTime time.Duration) {
	start := time.Now()
	var outp []byte
	var err error

	for {
		if t.failed {
			return
		}
		if time.Since(start) > maxTime {
			_, file, line, _ := runtime.Caller(1)
			fname := filepath.Base(file)
			if err != nil {
				t.Fatalf("%v:%v, %v (failed after %v with '%v'), output: '%v'", fname, line, blockName, time.Since(start), err, string(outp))
			}
			return
		}
		outp, err = f()
		if err == nil {
			return
		}
		time.Sleep(1000 * time.Millisecond)
	}
}

func once(blockName string, t *testing.T, f cmdFunc) {
	outp, err := f()
	if err != nil {
		t.Fatalf("%v with '%v', output: %v", blockName, err, string(outp))
	}
}

type server struct {
	cmd           *exec.Cmd
	t             *t
	envName       string
	portNum       int
	containerName string
	opts          options
}

func getFrame(skipFrames int) runtime.Frame {
	// We need the frame at index skipFrames+2, since we never want runtime.Callers and getFrame
	targetFrameIndex := skipFrames + 2

	// Set size to targetFrameIndex+2 to ensure we have room for one more caller than we need
	programCounters := make([]uintptr, targetFrameIndex+2)
	n := runtime.Callers(0, programCounters)

	frame := runtime.Frame{Function: "unknown"}
	if n > 0 {
		frames := runtime.CallersFrames(programCounters[:n])
		for more, frameIndex := true, 0; more && frameIndex <= targetFrameIndex; frameIndex++ {
			var frameCandidate runtime.Frame
			frameCandidate, more = frames.Next()
			if frameIndex == targetFrameIndex {
				frame = frameCandidate
			}
		}
	}

	return frame
}

// taken from https://stackoverflow.com/questions/35212985/is-it-possible-get-information-about-caller-function-in-golang
// MyCaller returns the caller of the function that called it :)
func myCaller() string {
	// Skip GetCallerFunctionName and the function to get the caller of
	return getFrame(2).Function
}

type options struct {
	auth string // eg. jwt
}

func newServer(t *t, opts ...options) server {
	min := 8000
	max := 60000
	portnum := rand.Intn(max-min) + min
	fname := strings.Split(myCaller(), ".")[2]

	// kill container, ignore error because it might not exist,
	// we dont care about this that much
	exec.Command("docker", "kill", fname).CombinedOutput()
	exec.Command("docker", "rm", fname).CombinedOutput()

	cmd := exec.Command("docker", "run",
		"-e", "MICRO_LOG_LEVEL=fatal",
		"--name", fname,
		"-p", fmt.Sprintf("%v:8081", portnum),
		"micro", "server")

	if len(opts) == 1 && opts[0].auth == "jwt" {

		base64 := "base64 -w0"
		if runtime.GOOS == "darwin" {
			base64 = "base64 -b0"
		}
		priv := "cat /tmp/sshkey | " + base64
		privKey, err := exec.Command("bash", "-c", priv).Output()
		if err != nil {
			panic(string(privKey))
		} else if len(privKey) == 0 {
			panic("privKey has not been set")
		}

		pub := "cat /tmp/sshkey.pub | " + base64
		pubKey, err := exec.Command("bash", "-c", pub).Output()
		if err != nil {
			panic(string(pubKey))
		} else if len(pubKey) == 0 {
			panic("pubKey has not been set")
		}
		cmd = exec.Command("docker", "run", "--name", fname,
			fmt.Sprintf("-p=%v:8081", portnum),
			"-e", "MICRO_LOG_LEVEL=fatal",
			"-e", "MICRO_AUTH=jwt",
			"-e", "MICRO_AUTH_PRIVATE_KEY="+strings.Trim(string(privKey), "\n"),
			"-e", "MICRO_AUTH_PUBLIC_KEY="+strings.Trim(string(pubKey), "\n"),
			"micro", "server")
	}
	//fmt.Println("docker", "run", "--name", fname, fmt.Sprintf("-p=%v:8081", portnum), "micro", "server")
	opt := options{}
	if len(opts) > 0 {
		opt = opts[0]
	}
	return server{
		cmd:           cmd,
		t:             t,
		envName:       fname,
		containerName: fname,
		portNum:       portnum,
		opts:          opt,
	}
}

func (s server) launch() {
	go func() {
		if err := s.cmd.Start(); err != nil {
			s.t.t.Fatal(err)
		}
	}()

	// add the environment
	try("Adding micro env", s.t, func() ([]byte, error) {
		outp, err := exec.Command("micro", "env", "add", s.envName, fmt.Sprintf("127.0.0.1:%v", s.portNum)).CombinedOutput()
		if err != nil {
			return outp, err
		}
		if len(outp) > 0 {
			return outp, errors.New("Not added")
		}

		outp, err = exec.Command("micro", "env").CombinedOutput()
		if err != nil {
			return outp, err
		}
		if !strings.Contains(string(outp), s.envName) {
			return outp, errors.New("Not added")
		}

		return outp, nil
	}, 15*time.Second)

	try("Calling micro server", s.t, func() ([]byte, error) {
		outp, err := exec.Command("micro", s.envFlag(), "list", "services").CombinedOutput()
		if !strings.Contains(string(outp), "runtime") ||
			!strings.Contains(string(outp), "registry") ||
			!strings.Contains(string(outp), "api") ||
			!strings.Contains(string(outp), "broker") ||
			!strings.Contains(string(outp), "config") ||
			!strings.Contains(string(outp), "debug") ||
			!strings.Contains(string(outp), "proxy") ||
			!strings.Contains(string(outp), "auth") ||
			!strings.Contains(string(outp), "store") {
			return outp, errors.New("Not ready")
		}

		return outp, err
	}, 60*time.Second)

	time.Sleep(5 * time.Second)
}

func (s server) close() {
	// remove the credentials so they aren't reused on next run
	token.Remove(s.envName)

	// reset back to the default namespace
	namespace.Set("micro", s.envName)

	exec.Command("docker", "kill", s.containerName).CombinedOutput()
	if s.cmd.Process != nil {
		s.cmd.Process.Signal(syscall.SIGKILL)
	}
}

func (s server) envFlag() string {
	return fmt.Sprintf("-env=%v", s.envName)
}

type t struct {
	counter int
	failed  bool
	format  string
	values  []interface{}
	t       *testing.T
}

func (t *t) Fatal(values ...interface{}) {
	t.t.Helper()
	t.t.Log(values...)
	t.failed = true
	t.values = values
}

func (t *t) Log(values ...interface{}) {
	t.t.Helper()
	t.t.Log(values...)
}

func (t *t) Fatalf(format string, values ...interface{}) {
	t.t.Helper()
	t.t.Log(fmt.Sprintf(format, values...))
	t.failed = true
	t.values = values
	t.format = format
}

func (t *t) Parallel() {
	if t.counter == 0 && isParallel {
		t.t.Parallel()
	}
	t.counter++
}

func newT(te *testing.T) *t {
	return &t{t: te}
}

// trySuite is designed to retry a TestXX function
func trySuite(t *testing.T, f func(t *t), times int) {
	t.Helper()
	tee := newT(t)
	for i := 0; i < times; i++ {
		f(tee)
		if !tee.failed {
			return
		}
		if i != times-1 {
			tee.failed = false
		}
		time.Sleep(200 * time.Millisecond)
	}
	if tee.failed {
		if len(tee.format) > 0 {
			t.Fatalf(tee.format, tee.values...)
		} else {
			t.Fatal(tee.values...)
		}
	}
}
<commit_msg>Revert "test: set log level"<commit_after>package test

import (
	"errors"
	"fmt"
	"math/rand"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"testing"
	"time"

	"github.com/micro/micro/v2/client/cli/namespace"
	"github.com/micro/micro/v2/client/cli/token"
)

const (
	retryCount = 2
	isParallel = true
)

type cmdFunc func() ([]byte, error)

// try is designed with command line executions in mind
func try(blockName string, t *t, f cmdFunc, maxTime time.Duration) {
	start := time.Now()
	var outp []byte
	var err error

	for {
		if t.failed {
			return
		}
		if time.Since(start) > maxTime {
			_, file, line, _ := runtime.Caller(1)
			fname := filepath.Base(file)
			if err != nil {
				t.Fatalf("%v:%v, %v (failed after %v with '%v'), output: '%v'", fname, line, blockName, time.Since(start), err, string(outp))
			}
			return
		}
		outp, err = f()
		if err == nil {
			return
		}
		time.Sleep(1000 * time.Millisecond)
	}
}

func once(blockName string, t *testing.T, f cmdFunc) {
	outp, err := f()
	if err != nil {
		t.Fatalf("%v with '%v', output: %v", blockName, err, string(outp))
	}
}

type server struct {
	cmd           *exec.Cmd
	t             *t
	envName       string
	portNum       int
	containerName string
	opts          options
}

func getFrame(skipFrames int) runtime.Frame {
	// We need the frame at index skipFrames+2, since we never want runtime.Callers and getFrame
	targetFrameIndex := skipFrames + 2

	// Set size to targetFrameIndex+2 to ensure we have room for one more caller than we need
	programCounters := make([]uintptr, targetFrameIndex+2)
	n := runtime.Callers(0, programCounters)

	frame := runtime.Frame{Function: "unknown"}
	if n > 0 {
		frames := runtime.CallersFrames(programCounters[:n])
		for more, frameIndex := true, 0; more && frameIndex <= targetFrameIndex; frameIndex++ {
			var frameCandidate runtime.Frame
			frameCandidate, more = frames.Next()
			if frameIndex == targetFrameIndex {
				frame = frameCandidate
			}
		}
	}

	return frame
}

// taken from https://stackoverflow.com/questions/35212985/is-it-possible-get-information-about-caller-function-in-golang
// MyCaller returns the caller of the function that called it :)
func myCaller() string {
	// Skip GetCallerFunctionName and the function to get the caller of
	return getFrame(2).Function
}

type options struct {
	auth string // eg. jwt
}

func newServer(t *t, opts ...options) server {
	min := 8000
	max := 60000
	portnum := rand.Intn(max-min) + min
	fname := strings.Split(myCaller(), ".")[2]

	// kill container, ignore error because it might not exist,
	// we dont care about this that much
	exec.Command("docker", "kill", fname).CombinedOutput()
	exec.Command("docker", "rm", fname).CombinedOutput()

	cmd := exec.Command("docker", "run", "--name", fname,
		fmt.Sprintf("-p=%v:8081", portnum), "micro", "server")
	if len(opts) == 1 && opts[0].auth == "jwt" {

		base64 := "base64 -w0"
		if runtime.GOOS == "darwin" {
			base64 = "base64 -b0"
		}
		priv := "cat /tmp/sshkey | " + base64
		privKey, err := exec.Command("bash", "-c", priv).Output()
		if err != nil {
			panic(string(privKey))
		} else if len(privKey) == 0 {
			panic("privKey has not been set")
		}

		pub := "cat /tmp/sshkey.pub | " + base64
		pubKey, err := exec.Command("bash", "-c", pub).Output()
		if err != nil {
			panic(string(pubKey))
		} else if len(pubKey) == 0 {
			panic("pubKey has not been set")
		}
		cmd = exec.Command("docker", "run", "--name", fname,
			fmt.Sprintf("-p=%v:8081", portnum),
			"-e", "MICRO_AUTH=jwt",
			"-e", "MICRO_AUTH_PRIVATE_KEY="+strings.Trim(string(privKey), "\n"),
			"-e", "MICRO_AUTH_PUBLIC_KEY="+strings.Trim(string(pubKey), "\n"),
			"micro", "server")
	}
	//fmt.Println("docker", "run", "--name", fname, fmt.Sprintf("-p=%v:8081", portnum), "micro", "server")
	opt := options{}
	if len(opts) > 0 {
		opt = opts[0]
	}
	return server{
		cmd:           cmd,
		t:             t,
		envName:       fname,
		containerName: fname,
		portNum:       portnum,
		opts:          opt,
	}
}

func (s server) launch() {
	go func() {
		if err := s.cmd.Start(); err != nil {
			s.t.t.Fatal(err)
		}
	}()

	// add the environment
	try("Adding micro env", s.t, func() ([]byte, error) {
		outp, err := exec.Command("micro", "env", "add", s.envName, fmt.Sprintf("127.0.0.1:%v", s.portNum)).CombinedOutput()
		if err != nil {
			return outp, err
		}
		if len(outp) > 0 {
			return outp, errors.New("Not added")
		}

		outp, err = exec.Command("micro", "env").CombinedOutput()
		if err != nil {
			return outp, err
		}
		if !strings.Contains(string(outp), s.envName) {
			return outp, errors.New("Not added")
		}

		return outp, nil
	}, 15*time.Second)

	try("Calling micro server", s.t, func() ([]byte, error) {
		outp, err := exec.Command("micro", s.envFlag(), "list", "services").CombinedOutput()
		if !strings.Contains(string(outp), "runtime") ||
			!strings.Contains(string(outp), "registry") ||
			!strings.Contains(string(outp), "api") ||
			!strings.Contains(string(outp), "broker") ||
			!strings.Contains(string(outp), "config") ||
			!strings.Contains(string(outp), "debug") ||
			!strings.Contains(string(outp), "proxy") ||
			!strings.Contains(string(outp), "auth") ||
			!strings.Contains(string(outp), "store") {
			return outp, errors.New("Not ready")
		}

		return outp, err
	}, 60*time.Second)

	time.Sleep(5 * time.Second)
}

func (s server) close() {
	// remove the credentials so they aren't reused on next run
	token.Remove(s.envName)

	// reset back to the default namespace
	namespace.Set("micro", s.envName)

	exec.Command("docker", "kill", s.containerName).CombinedOutput()
	if s.cmd.Process != nil {
		s.cmd.Process.Signal(syscall.SIGKILL)
	}
}

func (s server) envFlag() string {
	return fmt.Sprintf("-env=%v", s.envName)
}

type t struct {
	counter int
	failed  bool
	format  string
	values  []interface{}
	t       *testing.T
}

func (t *t) Fatal(values ...interface{}) {
	t.t.Helper()
	t.t.Log(values...)
	t.failed = true
	t.values = values
}

func (t *t) Log(values ...interface{}) {
	t.t.Helper()
	t.t.Log(values...)
}

func (t *t) Fatalf(format string, values ...interface{}) {
	t.t.Helper()
	t.t.Log(fmt.Sprintf(format, values...))
	t.failed = true
	t.values = values
	t.format = format
}

func (t *t) Parallel() {
	if t.counter == 0 && isParallel {
		t.t.Parallel()
	}
	t.counter++
}

func newT(te *testing.T) *t {
	return &t{t: te}
}

// trySuite is designed to retry a TestXX function
func trySuite(t *testing.T, f func(t *t), times int) {
	t.Helper()
	tee := newT(t)
	for i := 0; i < times; i++ {
		f(tee)
		if !tee.failed {
			return
		}
		if i != times-1 {
			tee.failed = false
		}
		time.Sleep(200 * time.Millisecond)
	}
	if tee.failed {
		if len(tee.format) > 0 {
			t.Fatalf(tee.format, tee.values...)
		} else {
			t.Fatal(tee.values...)
		}
	}
}
<|endoftext|>


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

package main

import (
	"flag"
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/coreos/coreos-cloudinit/config"
	"github.com/coreos/coreos-cloudinit/config/validate"
	"github.com/coreos/coreos-cloudinit/datasource"
	"github.com/coreos/coreos-cloudinit/datasource/configdrive"
	"github.com/coreos/coreos-cloudinit/datasource/file"
	"github.com/coreos/coreos-cloudinit/datasource/metadata/cloudsigma"
	"github.com/coreos/coreos-cloudinit/datasource/metadata/digitalocean"
	"github.com/coreos/coreos-cloudinit/datasource/metadata/ec2"
	"github.com/coreos/coreos-cloudinit/datasource/proc_cmdline"
	"github.com/coreos/coreos-cloudinit/datasource/url"
	"github.com/coreos/coreos-cloudinit/datasource/waagent"
	"github.com/coreos/coreos-cloudinit/initialize"
	"github.com/coreos/coreos-cloudinit/network"
	"github.com/coreos/coreos-cloudinit/pkg"
	"github.com/coreos/coreos-cloudinit/system"
)

const (
	version               = "1.4.1"
	datasourceInterval    = 100 * time.Millisecond
	datasourceMaxInterval = 30 * time.Second
	datasourceTimeout     = 5 * time.Minute
)

var (
	flags = struct {
		printVersion  bool
		ignoreFailure bool
		sources       struct {
			file                        string
			configDrive                 string
			waagent                     string
			metadataService             bool
			ec2MetadataService          string
			cloudSigmaMetadataService   bool
			digitalOceanMetadataService string
			url                         string
			procCmdLine                 bool
		}
		convertNetconf string
		workspace      string
		sshKeyName     string
		oem            string
		validate       bool
	}{}
)

func init() {
	flag.BoolVar(&flags.printVersion, "version", false, "Print the version and exit")
	flag.BoolVar(&flags.ignoreFailure, "ignore-failure", false, "Exits with 0 status in the event of malformed input from user-data")
	flag.StringVar(&flags.sources.file, "from-file", "", "Read user-data from provided file")
	flag.StringVar(&flags.sources.configDrive, "from-configdrive", "", "Read data from provided cloud-drive directory")
	flag.StringVar(&flags.sources.waagent, "from-waagent", "", "Read data from provided waagent directory")
	flag.BoolVar(&flags.sources.metadataService, "from-metadata-service", false, "[DEPRECATED - Use -from-ec2-metadata] Download data from metadata service")
	flag.StringVar(&flags.sources.ec2MetadataService, "from-ec2-metadata", "", "Download EC2 data from the provided url")
	flag.BoolVar(&flags.sources.cloudSigmaMetadataService, "from-cloudsigma-metadata", false, "Download data from CloudSigma server context")
	flag.StringVar(&flags.sources.digitalOceanMetadataService, "from-digitalocean-metadata", "", "Download DigitalOcean data from the provided url")
	flag.StringVar(&flags.sources.url, "from-url", "", "Download user-data from provided url")
	flag.BoolVar(&flags.sources.procCmdLine, "from-proc-cmdline", false, fmt.Sprintf("Parse %s for '%s=<url>', using the cloud-config served by an HTTP GET to <url>", proc_cmdline.ProcCmdlineLocation, proc_cmdline.ProcCmdlineCloudConfigFlag))
	flag.StringVar(&flags.oem, "oem", "", "Use the settings specific to the provided OEM")
	flag.StringVar(&flags.convertNetconf, "convert-netconf", "", "Read the network config provided in cloud-drive and translate it from the specified format into networkd unit files")
	flag.StringVar(&flags.workspace, "workspace", "/var/lib/coreos-cloudinit", "Base directory coreos-cloudinit should use to store data")
	flag.StringVar(&flags.sshKeyName, "ssh-key-name", initialize.DefaultSSHKeyName, "Add SSH keys to the system with the given name")
	flag.BoolVar(&flags.validate, "validate", false, "[EXPERIMENTAL] Validate the user-data but do not apply it to the system")
}

type oemConfig map[string]string

var (
	oemConfigs = map[string]oemConfig{
		"digitalocean": oemConfig{
			"from-digitalocean-metadata": "http://169.254.169.254/",
			"convert-netconf":            "digitalocean",
		},
		"ec2-compat": oemConfig{
			"from-ec2-metadata": "http://169.254.169.254/",
			"from-configdrive":  "/media/configdrive",
		},
		"rackspace-onmetal": oemConfig{
			"from-configdrive": "/media/configdrive",
			"convert-netconf":  "debian",
		},
		"azure": oemConfig{
			"from-waagent": "/var/lib/waagent",
		},
		"cloudsigma": oemConfig{
			"from-cloudsigma-metadata": "true",
		},
	}
)

func main() {
	failure := false

	flag.Parse()

	if c, ok := oemConfigs[flags.oem]; ok {
		for k, v := range c {
			flag.Set(k, v)
		}
	} else if flags.oem != "" {
		oems := make([]string, 0, len(oemConfigs))
		for k := range oemConfigs {
			oems = append(oems, k)
		}
		fmt.Printf("Invalid option to --oem: %q. Supported options: %q\n", flags.oem, oems)
		os.Exit(2)
	}

	if flags.printVersion == true {
		fmt.Printf("coreos-cloudinit version %s\n", version)
		os.Exit(0)
	}

	switch flags.convertNetconf {
	case "":
	case "debian":
	case "digitalocean":
	default:
		fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian, digitalocean'\n", flags.convertNetconf)
		os.Exit(2)
	}

	dss := getDatasources()
	if len(dss) == 0 {
		fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-ec2-metadata, --from-cloudsigma-metadata, --from-url or --from-proc-cmdline")
		os.Exit(2)
	}

	ds := selectDatasource(dss)
	if ds == nil {
		fmt.Println("No datasources available in time")
		os.Exit(1)
	}

	fmt.Printf("Fetching user-data from datasource of type %q\n", ds.Type())
	userdataBytes, err := ds.FetchUserdata()
	if err != nil {
		fmt.Printf("Failed fetching user-data from datasource: %v\nContinuing...\n", err)
		failure = true
	}

	if report, err := validate.Validate(userdataBytes); err == nil {
		ret := 0
		for _, e := range report.Entries() {
			fmt.Println(e)
			ret = 1
		}
		if flags.validate {
			os.Exit(ret)
		}
	} else {
		fmt.Printf("Failed while validating user_data (%q)\n", err)
		if flags.validate {
			os.Exit(1)
		}
	}

	fmt.Printf("Fetching meta-data from datasource of type %q\n", ds.Type())
	metadata, err := ds.FetchMetadata()
	if err != nil {
		fmt.Printf("Failed fetching meta-data from datasource: %v\n", err)
		os.Exit(1)
	}

	// Apply environment to user-data
	env := initialize.NewEnvironment("/", ds.ConfigRoot(), flags.workspace, flags.sshKeyName, metadata)
	userdata := env.Apply(string(userdataBytes))

	var ccu *config.CloudConfig
	var script *config.Script
	if ud, err := initialize.ParseUserData(userdata); err != nil {
		fmt.Printf("Failed to parse user-data: %v\nContinuing...\n", err)
		failure = true
	} else {
		switch t := ud.(type) {
		case *config.CloudConfig:
			ccu = t
		case *config.Script:
			script = t
		}
	}

	fmt.Println("Merging cloud-config from meta-data and user-data")
	cc := mergeConfigs(ccu, metadata)

	var ifaces []network.InterfaceGenerator
	if flags.convertNetconf != "" {
		var err error
		switch flags.convertNetconf {
		case "debian":
			ifaces, err = network.ProcessDebianNetconf(metadata.NetworkConfig)
		case "digitalocean":
			ifaces, err = network.ProcessDigitalOceanNetconf(metadata.NetworkConfig)
		default:
			err = fmt.Errorf("Unsupported network config format %q", flags.convertNetconf)
		}
		if err != nil {
			fmt.Printf("Failed to generate interfaces: %v\n", err)
			os.Exit(1)
		}
	}

	if err = initialize.Apply(cc, ifaces, env); err != nil {
		fmt.Printf("Failed to apply cloud-config: %v\n", err)
		os.Exit(1)
	}

	if script != nil {
		if err = runScript(*script, env); err != nil {
			fmt.Printf("Failed to run script: %v\n", err)
			os.Exit(1)
		}
	}

	if failure && !flags.ignoreFailure {
		os.Exit(1)
	}
}

// mergeConfigs merges certain options from md (meta-data from the datasource)
// onto cc (a CloudConfig derived from user-data), if they are not already set
// on cc (i.e. user-data always takes precedence)
func mergeConfigs(cc *config.CloudConfig, md datasource.Metadata) (out config.CloudConfig) {
	if cc != nil {
		out = *cc
	}

	if md.Hostname != "" {
		if out.Hostname != "" {
			fmt.Printf("Warning: user-data hostname (%s) overrides metadata hostname (%s)\n", out.Hostname, md.Hostname)
		} else {
			out.Hostname = md.Hostname
		}
	}
	for _, key := range md.SSHPublicKeys {
		out.SSHAuthorizedKeys = append(out.SSHAuthorizedKeys, key)
	}
	return
}

// getDatasources creates a slice of possible Datasources for cloudinit based
// on the different source command-line flags.
func getDatasources() []datasource.Datasource {
	dss := make([]datasource.Datasource, 0, 5)
	if flags.sources.file != "" {
		dss = append(dss, file.NewDatasource(flags.sources.file))
	}
	if flags.sources.url != "" {
		dss = append(dss, url.NewDatasource(flags.sources.url))
	}
	if flags.sources.configDrive != "" {
		dss = append(dss, configdrive.NewDatasource(flags.sources.configDrive))
	}
	if flags.sources.metadataService {
		dss = append(dss, ec2.NewDatasource(ec2.DefaultAddress))
	}
	if flags.sources.ec2MetadataService != "" {
		dss = append(dss, ec2.NewDatasource(flags.sources.ec2MetadataService))
	}
	if flags.sources.cloudSigmaMetadataService {
		dss = append(dss, cloudsigma.NewServerContextService())
	}
	if flags.sources.digitalOceanMetadataService != "" {
		dss = append(dss, digitalocean.NewDatasource(flags.sources.digitalOceanMetadataService))
	}
	if flags.sources.waagent != "" {
		dss = append(dss, waagent.NewDatasource(flags.sources.waagent))
	}
	if flags.sources.procCmdLine {
		dss = append(dss, proc_cmdline.NewDatasource())
	}
	return dss
}

// selectDatasource attempts to choose a valid Datasource to use based on its
// current availability. The first Datasource to report to be available is
// returned. Datasources will be retried if possible if they are not
// immediately available. If all Datasources are permanently unavailable or
// datasourceTimeout is reached before one becomes available, nil is returned.
func selectDatasource(sources []datasource.Datasource) datasource.Datasource {
	ds := make(chan datasource.Datasource)
	stop := make(chan struct{})
	var wg sync.WaitGroup

	for _, s := range sources {
		wg.Add(1)
		go func(s datasource.Datasource) {
			defer wg.Done()

			duration := datasourceInterval
			for {
				fmt.Printf("Checking availability of %q\n", s.Type())
				if s.IsAvailable() {
					ds <- s
					return
				} else if !s.AvailabilityChanges() {
					return
				}
				select {
				case <-stop:
					return
				case <-time.After(duration):
					duration = pkg.ExpBackoff(duration, datasourceMaxInterval)
				}
			}
		}(s)
	}

	done := make(chan struct{})
	go func() {
		wg.Wait()
		close(done)
	}()

	var s datasource.Datasource
	select {
	case s = <-ds:
	case <-done:
	case <-time.After(datasourceTimeout):
	}

	close(stop)
	return s
}

// TODO(jonboulle): this should probably be refactored and moved into a different module
func runScript(script config.Script, env *initialize.Environment) error {
	err := initialize.PrepWorkspace(env.Workspace())
	if err != nil {
		fmt.Printf("Failed preparing workspace: %v\n", err)
		return err
	}
	path, err := initialize.PersistScriptInWorkspace(script, env.Workspace())
	if err == nil {
		var name string
		name, err = system.ExecuteScript(path)
		initialize.PersistUnitNameInWorkspace(name, env.Workspace())
	}
	return err
}
<commit_msg>coreos-cloudinit: bump to v1.4.1+git<commit_after>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"flag"
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/coreos/coreos-cloudinit/config"
	"github.com/coreos/coreos-cloudinit/config/validate"
	"github.com/coreos/coreos-cloudinit/datasource"
	"github.com/coreos/coreos-cloudinit/datasource/configdrive"
	"github.com/coreos/coreos-cloudinit/datasource/file"
	"github.com/coreos/coreos-cloudinit/datasource/metadata/cloudsigma"
	"github.com/coreos/coreos-cloudinit/datasource/metadata/digitalocean"
	"github.com/coreos/coreos-cloudinit/datasource/metadata/ec2"
	"github.com/coreos/coreos-cloudinit/datasource/proc_cmdline"
	"github.com/coreos/coreos-cloudinit/datasource/url"
	"github.com/coreos/coreos-cloudinit/datasource/waagent"
	"github.com/coreos/coreos-cloudinit/initialize"
	"github.com/coreos/coreos-cloudinit/network"
	"github.com/coreos/coreos-cloudinit/pkg"
	"github.com/coreos/coreos-cloudinit/system"
)

const (
	version               = "1.4.1+git"
	datasourceInterval    = 100 * time.Millisecond
	datasourceMaxInterval = 30 * time.Second
	datasourceTimeout     = 5 * time.Minute
)

var (
	flags = struct {
		printVersion  bool
		ignoreFailure bool
		sources       struct {
			file                        string
			configDrive                 string
			waagent                     string
			metadataService             bool
			ec2MetadataService          string
			cloudSigmaMetadataService   bool
			digitalOceanMetadataService string
			url                         string
			procCmdLine                 bool
		}
		convertNetconf string
		workspace      string
		sshKeyName     string
		oem            string
		validate       bool
	}{}
)

func init() {
	flag.BoolVar(&flags.printVersion, "version", false, "Print the version and exit")
	flag.BoolVar(&flags.ignoreFailure, "ignore-failure", false, "Exits with 0 status in the event of malformed input from user-data")
	flag.StringVar(&flags.sources.file, "from-file", "", "Read user-data from provided file")
	flag.StringVar(&flags.sources.configDrive, "from-configdrive", "", "Read data from provided cloud-drive directory")
	flag.StringVar(&flags.sources.waagent, "from-waagent", "", "Read data from provided waagent directory")
	flag.BoolVar(&flags.sources.metadataService, "from-metadata-service", false, "[DEPRECATED - Use -from-ec2-metadata] Download data from metadata service")
	flag.StringVar(&flags.sources.ec2MetadataService, "from-ec2-metadata", "", "Download EC2 data from the provided url")
	flag.BoolVar(&flags.sources.cloudSigmaMetadataService, "from-cloudsigma-metadata", false, "Download data from CloudSigma server context")
	flag.StringVar(&flags.sources.digitalOceanMetadataService, "from-digitalocean-metadata", "", "Download DigitalOcean data from the provided url")
	flag.StringVar(&flags.sources.url, "from-url", "", "Download user-data from provided url")
	flag.BoolVar(&flags.sources.procCmdLine, "from-proc-cmdline", false, fmt.Sprintf("Parse %s for '%s=<url>', using the cloud-config served by an HTTP GET to <url>", proc_cmdline.ProcCmdlineLocation, proc_cmdline.ProcCmdlineCloudConfigFlag))
	flag.StringVar(&flags.oem, "oem", "", "Use the settings specific to the provided OEM")
	flag.StringVar(&flags.convertNetconf, "convert-netconf", "", "Read the network config provided in cloud-drive and translate it from the specified format into networkd unit files")
	flag.StringVar(&flags.workspace, "workspace", "/var/lib/coreos-cloudinit", "Base directory coreos-cloudinit should use to store data")
	flag.StringVar(&flags.sshKeyName, "ssh-key-name", initialize.DefaultSSHKeyName, "Add SSH keys to the system with the given name")
	flag.BoolVar(&flags.validate, "validate", false, "[EXPERIMENTAL] Validate the user-data but do not apply it to the system")
}

type oemConfig map[string]string

var (
	oemConfigs = map[string]oemConfig{
		"digitalocean": oemConfig{
			"from-digitalocean-metadata": "http://169.254.169.254/",
			"convert-netconf":            "digitalocean",
		},
		"ec2-compat": oemConfig{
			"from-ec2-metadata": "http://169.254.169.254/",
			"from-configdrive":  "/media/configdrive",
		},
		"rackspace-onmetal": oemConfig{
			"from-configdrive": "/media/configdrive",
			"convert-netconf":  "debian",
		},
		"azure": oemConfig{
			"from-waagent": "/var/lib/waagent",
		},
		"cloudsigma": oemConfig{
			"from-cloudsigma-metadata": "true",
		},
	}
)

func main() {
	failure := false

	flag.Parse()

	if c, ok := oemConfigs[flags.oem]; ok {
		for k, v := range c {
			flag.Set(k, v)
		}
	} else if flags.oem != "" {
		oems := make([]string, 0, len(oemConfigs))
		for k := range oemConfigs {
			oems = append(oems, k)
		}
		fmt.Printf("Invalid option to --oem: %q. Supported options: %q\n", flags.oem, oems)
		os.Exit(2)
	}

	if flags.printVersion == true {
		fmt.Printf("coreos-cloudinit version %s\n", version)
		os.Exit(0)
	}

	switch flags.convertNetconf {
	case "":
	case "debian":
	case "digitalocean":
	default:
		fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian, digitalocean'\n", flags.convertNetconf)
		os.Exit(2)
	}

	dss := getDatasources()
	if len(dss) == 0 {
		fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-ec2-metadata, --from-cloudsigma-metadata, --from-url or --from-proc-cmdline")
		os.Exit(2)
	}

	ds := selectDatasource(dss)
	if ds == nil {
		fmt.Println("No datasources available in time")
		os.Exit(1)
	}

	fmt.Printf("Fetching user-data from datasource of type %q\n", ds.Type())
	userdataBytes, err := ds.FetchUserdata()
	if err != nil {
		fmt.Printf("Failed fetching user-data from datasource: %v\nContinuing...\n", err)
		failure = true
	}

	if report, err := validate.Validate(userdataBytes); err == nil {
		ret := 0
		for _, e := range report.Entries() {
			fmt.Println(e)
			ret = 1
		}
		if flags.validate {
			os.Exit(ret)
		}
	} else {
		fmt.Printf("Failed while validating user_data (%q)\n", err)
		if flags.validate {
			os.Exit(1)
		}
	}

	fmt.Printf("Fetching meta-data from datasource of type %q\n", ds.Type())
	metadata, err := ds.FetchMetadata()
	if err != nil {
		fmt.Printf("Failed fetching meta-data from datasource: %v\n", err)
		os.Exit(1)
	}

	// Apply environment to user-data
	env := initialize.NewEnvironment("/", ds.ConfigRoot(), flags.workspace, flags.sshKeyName, metadata)
	userdata := env.Apply(string(userdataBytes))

	var ccu *config.CloudConfig
	var script *config.Script
	if ud, err := initialize.ParseUserData(userdata); err != nil {
		fmt.Printf("Failed to parse user-data: %v\nContinuing...\n", err)
		failure = true
	} else {
		switch t := ud.(type) {
		case *config.CloudConfig:
			ccu = t
		case *config.Script:
			script = t
		}
	}

	fmt.Println("Merging cloud-config from meta-data and user-data")
	cc := mergeConfigs(ccu, metadata)

	var ifaces []network.InterfaceGenerator
	if flags.convertNetconf != "" {
		var err error
		switch flags.convertNetconf {
		case "debian":
			ifaces, err = network.ProcessDebianNetconf(metadata.NetworkConfig)
		case "digitalocean":
			ifaces, err = network.ProcessDigitalOceanNetconf(metadata.NetworkConfig)
		default:
			err = fmt.Errorf("Unsupported network config format %q", flags.convertNetconf)
		}
		if err != nil {
			fmt.Printf("Failed to generate interfaces: %v\n", err)
			os.Exit(1)
		}
	}

	if err = initialize.Apply(cc, ifaces, env); err != nil {
		fmt.Printf("Failed to apply cloud-config: %v\n", err)
		os.Exit(1)
	}

	if script != nil {
		if err = runScript(*script, env); err != nil {
			fmt.Printf("Failed to run script: %v\n", err)
			os.Exit(1)
		}
	}

	if failure && !flags.ignoreFailure {
		os.Exit(1)
	}
}

// mergeConfigs merges certain options from md (meta-data from the datasource)
// onto cc (a CloudConfig derived from user-data), if they are not already set
// on cc (i.e. user-data always takes precedence)
func mergeConfigs(cc *config.CloudConfig, md datasource.Metadata) (out config.CloudConfig) {
	if cc != nil {
		out = *cc
	}

	if md.Hostname != "" {
		if out.Hostname != "" {
			fmt.Printf("Warning: user-data hostname (%s) overrides metadata hostname (%s)\n", out.Hostname, md.Hostname)
		} else {
			out.Hostname = md.Hostname
		}
	}
	for _, key := range md.SSHPublicKeys {
		out.SSHAuthorizedKeys = append(out.SSHAuthorizedKeys, key)
	}
	return
}

// getDatasources creates a slice of possible Datasources for cloudinit based
// on the different source command-line flags.
func getDatasources() []datasource.Datasource {
	dss := make([]datasource.Datasource, 0, 5)
	if flags.sources.file != "" {
		dss = append(dss, file.NewDatasource(flags.sources.file))
	}
	if flags.sources.url != "" {
		dss = append(dss, url.NewDatasource(flags.sources.url))
	}
	if flags.sources.configDrive != "" {
		dss = append(dss, configdrive.NewDatasource(flags.sources.configDrive))
	}
	if flags.sources.metadataService {
		dss = append(dss, ec2.NewDatasource(ec2.DefaultAddress))
	}
	if flags.sources.ec2MetadataService != "" {
		dss = append(dss, ec2.NewDatasource(flags.sources.ec2MetadataService))
	}
	if flags.sources.cloudSigmaMetadataService {
		dss = append(dss, cloudsigma.NewServerContextService())
	}
	if flags.sources.digitalOceanMetadataService != "" {
		dss = append(dss, digitalocean.NewDatasource(flags.sources.digitalOceanMetadataService))
	}
	if flags.sources.waagent != "" {
		dss = append(dss, waagent.NewDatasource(flags.sources.waagent))
	}
	if flags.sources.procCmdLine {
		dss = append(dss, proc_cmdline.NewDatasource())
	}
	return dss
}

// selectDatasource attempts to choose a valid Datasource to use based on its
// current availability. The first Datasource to report to be available is
// returned. Datasources will be retried if possible if they are not
// immediately available. If all Datasources are permanently unavailable or
// datasourceTimeout is reached before one becomes available, nil is returned.
func selectDatasource(sources []datasource.Datasource) datasource.Datasource {
	ds := make(chan datasource.Datasource)
	stop := make(chan struct{})
	var wg sync.WaitGroup

	for _, s := range sources {
		wg.Add(1)
		go func(s datasource.Datasource) {
			defer wg.Done()

			duration := datasourceInterval
			for {
				fmt.Printf("Checking availability of %q\n", s.Type())
				if s.IsAvailable() {
					ds <- s
					return
				} else if !s.AvailabilityChanges() {
					return
				}
				select {
				case <-stop:
					return
				case <-time.After(duration):
					duration = pkg.ExpBackoff(duration, datasourceMaxInterval)
				}
			}
		}(s)
	}

	done := make(chan struct{})
	go func() {
		wg.Wait()
		close(done)
	}()

	var s datasource.Datasource
	select {
	case s = <-ds:
	case <-done:
	case <-time.After(datasourceTimeout):
	}

	close(stop)
	return s
}

// TODO(jonboulle): this should probably be refactored and moved into a different module
func runScript(script config.Script, env *initialize.Environment) error {
	err := initialize.PrepWorkspace(env.Workspace())
	if err != nil {
		fmt.Printf("Failed preparing workspace: %v\n", err)
		return err
	}
	path, err := initialize.PersistScriptInWorkspace(script, env.Workspace())
	if err == nil {
		var name string
		name, err = system.ExecuteScript(path)
		initialize.PersistUnitNameInWorkspace(name, env.Workspace())
	}
	return err
}
<|endoftext|>


<commit_before>package routes

import (
	"io"
	"net/http"

	"github.com/gorilla/mux"
)

type invite struct {
	srcReq chan *http.Request
	srcRes chan http.ResponseWriter

	destReq chan *http.Request
	destRes chan http.ResponseWriter
}

func newInvite() *invite {
	return &invite{
		make(chan *http.Request),
		make(chan http.ResponseWriter),
		make(chan *http.Request),
		make(chan http.ResponseWriter),
	}
}

var invites = make(map[string]*invite)

func InviteGet(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	key := vars["key"]
	deviceType := vars["device"]

	inv, ok := invites[key]

	if !ok {
		inv = newInvite()
		invites[key] = inv
	}

	var err error
	if deviceType == "src" {
		inv.srcRes <- w
		<-inv.srcReq
	} else {
		inv.destRes <- w
		<-inv.destReq
	}
	if err != nil {
		serverError(w)
	}
}

func InvitePost(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	key := vars["key"]
	deviceType := vars["device"]

	inv, ok := invites[key]

	if !ok {
		inv = newInvite()
		invites[key] = inv
	}

	var err error
	if deviceType == "src" {
		res := <-inv.destRes
		_, err = io.Copy(res, r.Body)
		inv.destReq <- r
	} else {
		res := <-inv.srcRes
		_, err = io.Copy(res, r.Body)
		inv.srcReq <- r
	}
	if err != nil {
		serverError(w)
		return
	}
}
<commit_msg>free up invite keys when no connections are present<commit_after>package routes

import (
	"io"
	"net/http"

	"sync"

	"log"

	"github.com/gorilla/mux"
)

type Invites struct {
	sync.RWMutex
	invites map[string]*invite
}

func NewInvites() *Invites {
	return &Invites{
		invites: make(map[string]*invite),
	}
}

// Get or create an invite
func (i *Invites) Get(key string) *invite {
	i.RLock()
	inv, ok := i.invites[key]
	i.RUnlock()

	if !ok {

		inv = &invite{
			sync.WaitGroup{},
			make(chan *http.Request),
			make(chan http.ResponseWriter),
			make(chan *http.Request),
			make(chan http.ResponseWriter),
		}

		i.Lock()
		i.invites[key] = inv
		i.Unlock()

		inv.wg.Add(1)

		go func() {
			inv.wg.Wait()
			log.Printf("invite key freed: %v", key)
			delete(i.invites, key)
		}()
	} else {
		inv.wg.Add(1)
	}

	return inv
}

type invite struct {
	wg sync.WaitGroup

	srcReq chan *http.Request
	srcRes chan http.ResponseWriter

	destReq chan *http.Request
	destRes chan http.ResponseWriter
}

var invites = NewInvites()

func InviteGet(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	key := vars["key"]
	deviceType := vars["device"]

	inv := invites.Get(key)
	defer inv.wg.Done()
	defer log.Println("req done")

	var err error
	if deviceType == "src" {
		inv.srcRes <- w
		<-inv.srcReq
	} else {
		inv.destRes <- w
		<-inv.destReq
	}
	if err != nil {
		serverError(w)
	}
}

func InvitePost(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	key := vars["key"]
	deviceType := vars["device"]

	inv := invites.Get(key)
	defer inv.wg.Done()
	defer log.Println("post done")

	var err error
	if deviceType == "src" {
		res := <-inv.destRes
		_, err = io.Copy(res, r.Body)
		inv.destReq <- r
	} else {
		res := <-inv.srcRes
		_, err = io.Copy(res, r.Body)
		inv.srcReq <- r
	}
	if err != nil {
		serverError(w)
		return
	}
}
<|endoftext|>