<commit_before>// Make balanced rosters according to weighted criteria

package main

import (
	"log"
	"math/rand"
	"sort"
	"time"

	"gopkg.in/alecthomas/kingpin.v2"

	"github.com/topher200/baseutil"
)

// Number of teams to break players into
const numTeams = 6

// Number of times to run our genetic algorithm
const numRuns = 100000

// Percent of the time we will try to mutate. After each mutation, we have a
// mutationChance percent chance of mutating again.
const mutationChance = 5

type Score float64
type Solution struct {
	players []Player
	score   Score
}

// Implement sort.Interface for []Solution, sorting based on score
type ByScore []Solution

func (a ByScore) Len() int {
	return len(a)
}
func (a ByScore) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}
func (a ByScore) Less(i, j int) bool {
	return a[i].score < a[j].score
}

type Team struct {
	players []Player
}

func splitIntoTeams(players []Player) []Team {
	teams := make([]Team, numTeams)
	for _, player := range players {
		teams[player.team].players = append(teams[player.team].players, player)
	}
	return teams
}

type criterionCalculationFunction func(teams []Team) Score
type playerFilter func(player Player) bool
type Criterion struct {
	name      string                       // human readable name
	calculate criterionCalculationFunction // how to calculate the raw score
	filter    playerFilter                 // cull down to players that match
	weight    int                          // how much weight to give this score
}

func playerCountDifference(teams []Team) Score {
	teamLengths := make([]int, numTeams)
	for i, team := range teams {
		teamLengths[i] = len(team.players)
	}
	return Score(baseutil.StandardDeviationInt(teamLengths))
}

// runCriterion by filtering the input teams and running the criterion function
func runCriterion(criterion Criterion, teams []Team) Score {
	filteredTeams := make([]Team, len(teams))
	for i, team := range teams {
		for _, player := range team.players {
			if criterion.filter == nil || criterion.filter(player) {
				filteredTeams[i].players = append(filteredTeams[i].players, player)
			}
		}
	}
	rawScore := criterion.calculate(filteredTeams)
	weightedScore := Score(float64(rawScore) * float64(criterion.weight))
	log.Printf("Scoring '%s'. Raw score %f, weighted score %f\n",
		criterion.name, rawScore, weightedScore)
	return weightedScore
}

// Score a solution based on weighted criteria.
func score(players []Player) Score {
	teams := splitIntoTeams(players)

	// Balanced by number
	// TODO
	// Score on balance in gender.
	//
	// For each Gender we make a list of the number of players of that gender on
	// each team. Then we take the standard deviation of those two lists to
	// determine the gender imbalance.
	balancedByPlayerCount := runCriterion(
		Criterion{
			"number of players", playerCountDifference, nil, 10}, teams)
	balancedByPlayerCountMales := runCriterion(
		Criterion{
			"number of males", playerCountDifference, IsMale, 9}, teams)
	balancedByPlayerCountFemales := runCriterion(
		Criterion{
			"number of females", playerCountDifference, IsFemale, 9}, teams)

	totalScore := balancedByPlayerCount + balancedByPlayerCountMales +
		balancedByPlayerCountFemales

	return totalScore
}

func randomizeTeams(players []Player) {
	for i, _ := range players {
		players[i].team = uint8(rand.Intn(numTeams))
	}
}

// Mutate the solution by moving random players to random teams, sometimes.
func mutate(players []Player) {
	for {
		// We have mutationChance of mutating. Otherwise, we break out of our loop
		if rand.Intn(100) > mutationChance {
			return
		}
		// Mutation! Move a random player to a random new team
		players[rand.Intn(len(players))].team = uint8(rand.Intn(numTeams))
	}
}

// Breed via combining the two given solutions, then randomly mutating.
func breed(solution1 Solution, solution2 Solution) Solution {
	// Create the new solution by taking crossover from both inputs
	newPlayers := make([]Player, len(solution1.players))
	for i, _ := range newPlayers {
		// Randomly take each player from solution1 or solution2
		if rand.Intn(100) < 50 {
			newPlayers[i] = solution1.players[i]
		} else {
			newPlayers[i] = solution2.players[i]
		}
	}

	// Mutate the new player list
	mutate(newPlayers)

	return Solution{newPlayers, score(newPlayers)}
}

func main() {
	// Read command line input
	filenamePointer := kingpin.Arg("input-file",
		"filename from which to get list of players").
		Required().String()
	deterministicPointer := kingpin.Flag("deterministic",
		"makes our output deterministic by allowing the default rand.Seed").
		Short('d').Bool()
	kingpin.Parse()

	if !*deterministicPointer {
		rand.Seed(time.Now().UTC().UnixNano())
	}

	players := ParsePlayers(*filenamePointer)
	if len(players) == 0 {
		panic("Could not find players")
	}

	// Create two random solutions to start
	topSolutions := make([]Solution, 2)
	for i, _ := range topSolutions {
		ourPlayers := make([]Player, len(players))
		copy(ourPlayers, players)
		randomizeTeams(ourPlayers)
		topSolutions[i] = Solution{ourPlayers, score(ourPlayers)}
	}

	topScore := topSolutions[0].score
	for i := 0; i < numRuns; i++ {
		if topScore != topSolutions[0].score {
			topScore = topSolutions[0].score
			log.Println("New top score! Run number ", i, "Score:", topScore)
		}

		// Create new solutions by breeding the top two solutions
		newSolutions := make([]Solution, 20)
		for i, _ := range newSolutions {
			if i <= 1 {
				// Keep the top solutions from last time - elitism!
				newSolutions[i] = topSolutions[i]
			} else {
				// Make a new solution based on our top two from last time
				newSolutions[i] = breed(topSolutions[0], topSolutions[1])
			}
		}

		// Of all the solutions we now have, save only our best two
		sort.Sort(ByScore(newSolutions))
		topSolutions[0], topSolutions[1] = newSolutions[0], newSolutions[1]
	}
}
<commit_msg>reduce number of runs<commit_after>// Make balanced rosters according to weighted criteria

package main

import (
	"log"
	"math/rand"
	"sort"
	"time"

	"gopkg.in/alecthomas/kingpin.v2"

	"github.com/topher200/baseutil"
)

// Number of teams to break players into
const numTeams = 6

// Number of times to run our genetic algorithm
const numRuns = 100

// Percent of the time we will try to mutate. After each mutation, we have a
// mutationChance percent chance of mutating again.
const mutationChance = 5

type Score float64
type Solution struct {
	players []Player
	score   Score
}

// Implement sort.Interface for []Solution, sorting based on score
type ByScore []Solution

func (a ByScore) Len() int {
	return len(a)
}
func (a ByScore) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}
func (a ByScore) Less(i, j int) bool {
	return a[i].score < a[j].score
}

type Team struct {
	players []Player
}

func splitIntoTeams(players []Player) []Team {
	teams := make([]Team, numTeams)
	for _, player := range players {
		teams[player.team].players = append(teams[player.team].players, player)
	}
	return teams
}

type criterionCalculationFunction func(teams []Team) Score
type playerFilter func(player Player) bool
type Criterion struct {
	name      string                       // human readable name
	calculate criterionCalculationFunction // how to calculate the raw score
	filter    playerFilter                 // cull down to players that match
	weight    int                          // how much weight to give this score
}

func playerCountDifference(teams []Team) Score {
	teamLengths := make([]int, numTeams)
	for i, team := range teams {
		teamLengths[i] = len(team.players)
	}
	return Score(baseutil.StandardDeviationInt(teamLengths))
}

// runCriterion by filtering the input teams and running the criterion function
func runCriterion(criterion Criterion, teams []Team) Score {
	filteredTeams := make([]Team, len(teams))
	for i, team := range teams {
		for _, player := range team.players {
			if criterion.filter == nil || criterion.filter(player) {
				filteredTeams[i].players = append(filteredTeams[i].players, player)
			}
		}
	}
	rawScore := criterion.calculate(filteredTeams)
	weightedScore := Score(float64(rawScore) * float64(criterion.weight))
	log.Printf("Scoring '%s'. Raw score %f, weighted score %f\n",
		criterion.name, rawScore, weightedScore)
	return weightedScore
}

// Score a solution based on weighted criteria.
func score(players []Player) Score {
	teams := splitIntoTeams(players)

	// Balanced by number
	// TODO
	// Score on balance in gender.
	//
	// For each Gender we make a list of the number of players of that gender on
	// each team. Then we take the standard deviation of those two lists to
	// determine the gender imbalance.
	balancedByPlayerCount := runCriterion(
		Criterion{
			"number of players", playerCountDifference, nil, 10}, teams)
	balancedByPlayerCountMales := runCriterion(
		Criterion{
			"number of males", playerCountDifference, IsMale, 9}, teams)
	balancedByPlayerCountFemales := runCriterion(
		Criterion{
			"number of females", playerCountDifference, IsFemale, 9}, teams)

	totalScore := balancedByPlayerCount + balancedByPlayerCountMales +
		balancedByPlayerCountFemales

	return totalScore
}

func randomizeTeams(players []Player) {
	for i, _ := range players {
		players[i].team = uint8(rand.Intn(numTeams))
	}
}

// Mutate the solution by moving random players to random teams, sometimes.
func mutate(players []Player) {
	for {
		// We have mutationChance of mutating. Otherwise, we break out of our loop
		if rand.Intn(100) > mutationChance {
			return
		}
		// Mutation! Move a random player to a random new team
		players[rand.Intn(len(players))].team = uint8(rand.Intn(numTeams))
	}
}

// Breed via combining the two given solutions, then randomly mutating.
func breed(solution1 Solution, solution2 Solution) Solution {
	// Create the new solution by taking crossover from both inputs
	newPlayers := make([]Player, len(solution1.players))
	for i, _ := range newPlayers {
		// Randomly take each player from solution1 or solution2
		if rand.Intn(100) < 50 {
			newPlayers[i] = solution1.players[i]
		} else {
			newPlayers[i] = solution2.players[i]
		}
	}

	// Mutate the new player list
	mutate(newPlayers)

	return Solution{newPlayers, score(newPlayers)}
}

func main() {
	// Read command line input
	filenamePointer := kingpin.Arg("input-file",
		"filename from which to get list of players").
		Required().String()
	deterministicPointer := kingpin.Flag("deterministic",
		"makes our output deterministic by allowing the default rand.Seed").
		Short('d').Bool()
	kingpin.Parse()

	if !*deterministicPointer {
		rand.Seed(time.Now().UTC().UnixNano())
	}

	players := ParsePlayers(*filenamePointer)
	if len(players) == 0 {
		panic("Could not find players")
	}

	// Create two random solutions to start
	topSolutions := make([]Solution, 2)
	for i, _ := range topSolutions {
		ourPlayers := make([]Player, len(players))
		copy(ourPlayers, players)
		randomizeTeams(ourPlayers)
		topSolutions[i] = Solution{ourPlayers, score(ourPlayers)}
	}

	topScore := topSolutions[0].score
	for i := 0; i < numRuns; i++ {
		if topScore != topSolutions[0].score {
			topScore = topSolutions[0].score
			log.Println("New top score! Run number ", i, "Score:", topScore)
		}

		// Create new solutions by breeding the top two solutions
		newSolutions := make([]Solution, 20)
		for i, _ := range newSolutions {
			if i <= 1 {
				// Keep the top solutions from last time - elitism!
				newSolutions[i] = topSolutions[i]
			} else {
				// Make a new solution based on our top two from last time
				newSolutions[i] = breed(topSolutions[0], topSolutions[1])
			}
		}

		// Of all the solutions we now have, save only our best two
		sort.Sort(ByScore(newSolutions))
		topSolutions[0], topSolutions[1] = newSolutions[0], newSolutions[1]
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package wallet

import (
	"fmt"

	"github.com/FactomProject/factom"
	"github.com/FactomProject/factomd/common/factoid"
	"github.com/FactomProject/factomd/common/interfaces"
	"github.com/FactomProject/factomd/common/primitives"
	"github.com/FactomProject/factomd/database/databaseOverlay"
	"github.com/FactomProject/factomd/database/hybridDB"
)

// Database keys and key prefixes
var (
	fblockDBPrefix = []byte("FBlock")
)

type TXDatabaseOverlay struct {
	dbo databaseOverlay.Overlay
}

func NewTXOverlay(db interfaces.IDatabase) *TXDatabaseOverlay {
	answer := new(TXDatabaseOverlay)
	answer.dbo.DB = db
	return answer
}

func NewTXLevelDB(ldbpath string) (*TXDatabaseOverlay, error) {
	db, err := hybridDB.NewLevelMapHybridDB(ldbpath, false)
	if err != nil {
		fmt.Printf("err opening transaction db: %v\n", err)
	}

	if db == nil {
		fmt.Println("Creating new transaction db ...")
		db, err = hybridDB.NewLevelMapHybridDB(ldbpath, true)

		if err != nil {
			return nil, err
		}
	}
	fmt.Println("Transaction database started from: " + ldbpath)
	return NewTXOverlay(db), nil
}

func NewTXBoltDB(boltPath string) (*TXDatabaseOverlay, error) {
	db := hybridDB.NewBoltMapHybridDB(nil, boltPath)

	fmt.Println("Database started from: " + boltPath)
	return NewTXOverlay(db), nil
}

func (db *TXDatabaseOverlay) Close() error {
	return db.dbo.Close()
}

// GetAllTXs returns a list of all transactions in the history of Factom. A
// local database is used to cache the factoid blocks.
func (db *TXDatabaseOverlay) GetAllTXs() ([]interfaces.ITransaction, error) {
	// update the database and get the newest fblock
	newest, err := db.update()
	if err != nil {
		return nil, err
	}
	fblock, err := db.GetFBlock(newest)
	if err != nil {
		return nil, err
	}
	txs := make([]interfaces.ITransaction, 0)

	for {
		// get all of the txs from the block
		height := fblock.GetDatabaseHeight()
		for _, tx := range fblock.GetTransactions() {
			ins, err := tx.TotalInputs()
			if err != nil {
				return nil, err
			}
			outs, err := tx.TotalOutputs()
			if err != nil {
				return nil, err
			}

			if ins != 0 || outs != 0 {
				tx.SetBlockHeight(height)
				txs = append(txs, tx)
			}
		}

		if pre := fblock.GetPrevKeyMR().String(); pre != factom.ZeroHash {
			// get the previous block
			fblock, err = db.GetFBlock(pre)
			if err != nil {
				return nil, err
			} else if fblock == nil {
				return nil, fmt.Errorf("Missing fblock in database: %s", pre)
			}
		} else {
			break
		}
	}

	return txs, nil
}

// GetTX gets a transaction by the transaction id
func (db *TXDatabaseOverlay) GetTX(txid string) (
	interfaces.ITransaction, error) {
	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if tx.GetSigHash().String() == txid {
			return tx, nil
		}
	}

	return nil, fmt.Errorf("Transaction not found")
}

// GetTXAddress returns a list of all transactions in the history of Factom that
// include a specific address.
func (db *TXDatabaseOverlay) GetTXAddress(adr string) (
	[]interfaces.ITransaction, error) {
	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		for _, in := range tx.GetInputs() {
			if primitives.ConvertFctAddressToUserStr(
				in.GetAddress()) == adr {
				txs = append(filtered, tx)
			}
		}
		for _, out := range tx.GetOutputs() {
			if primitives.ConvertFctAddressToUserStr(
				out.GetAddress()) == adr {
				txs = append(filtered, tx)
			}
		}
		for _, out := range tx.GetECOutputs() {
			if primitives.ConvertECAddressToUserStr(
				out.GetAddress()) == adr {
				txs = append(filtered, tx)
			}
		}
	}

	return filtered, nil
}

func (db *TXDatabaseOverlay) GetTXRange(start, end int) (
	[]interfaces.ITransaction, error) {
	if start < 0 || end < 0 {
		return nil, fmt.Errorf("Range cannot have negative numbers")
	}
	s, e := uint32(start), uint32(end)

	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if s <= tx.GetBlockHeight() && tx.GetBlockHeight() <= e {
			filtered = append(filtered, tx)
		}
	}

	return filtered, nil
}

// GetFBlock retrives a Factoid Block from Factom
func (db *TXDatabaseOverlay) GetFBlock(keymr string) (interfaces.IFBlock, error) {
	fblock := new(factoid.FBlock)
	data, err := db.dbo.Get(fblockDBPrefix, []byte(keymr), fblock)
	if err != nil {
		return nil, err
	}
	if data == nil {
		return nil, nil
	}
	return data.(interfaces.IFBlock), nil
}

func (db *TXDatabaseOverlay) InsertFBlock(fblock interfaces.IFBlock) error {
	fblockmr := []byte(fblock.GetKeyMR().String())

	batch := []interfaces.Record{}
	batch = append(batch, interfaces.Record{fblockDBPrefix, fblockmr, fblock})

	return db.dbo.PutInBatch(batch)
}

// update gets all fblocks written since the database was last updated, and
// returns the most recent fblock keymr.
func (db *TXDatabaseOverlay) update() (string, error) {
	fblock, err := fblockHead()
	if err != nil {
		return "", err
	}
	newest := fblock.GetKeyMR().String()

	prevmr := fblock.GetPrevKeyMR().String()
	for prevmr != factom.ZeroHash {
		//		// stop when we reach an fblock that is already in the db
		//		if f, err := db.GetFBlock(prevmr); err != nil {
		//			return "", err
		//		} else if f != nil {
		//			return newest, nil
		//		}

		// add the fblock to the db
		if err := db.InsertFBlock(fblock); err != nil {
			return "", err
		}

		fblock, err = getfblock(prevmr)
		if err != nil {
			return "", err
		}
		prevmr = fblock.GetPrevKeyMR().String()
	}

	// write the last fblock into the db
	if err := db.InsertFBlock(fblock); err != nil {
		return "", err
	}

	return newest, nil
}

// fblockHead gets the most recent fblock.
func fblockHead() (interfaces.IFBlock, error) {
	fblockID := "000000000000000000000000000000000000000000000000000000000000000f"

	dbhead, err := factom.GetDBlockHead()
	if err != nil {
		return nil, err
	}
	dblock, err := factom.GetDBlock(dbhead)
	if err != nil {
		return nil, err
	}

	var fblockmr string
	for _, eblock := range dblock.EntryBlockList {
		if eblock.ChainID == fblockID {
			fblockmr = eblock.KeyMR
		}
	}
	if fblockmr == "" {
		return nil, err
	}

	return getfblock(fblockmr)
}

func getfblock(keymr string) (interfaces.IFBlock, error) {
	p, err := factom.GetRaw(keymr)
	if err != nil {
		return nil, err
	}
	return factoid.UnmarshalFBlock(p)
}
<commit_msg>formatting<commit_after>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package wallet

import (
	"fmt"

	"github.com/FactomProject/factom"
	"github.com/FactomProject/factomd/common/factoid"
	"github.com/FactomProject/factomd/common/interfaces"
	"github.com/FactomProject/factomd/common/primitives"
	"github.com/FactomProject/factomd/database/databaseOverlay"
	"github.com/FactomProject/factomd/database/hybridDB"
)

// Database keys and key prefixes
var (
	fblockDBPrefix = []byte("FBlock")
)

type TXDatabaseOverlay struct {
	dbo databaseOverlay.Overlay
}

func NewTXOverlay(db interfaces.IDatabase) *TXDatabaseOverlay {
	answer := new(TXDatabaseOverlay)
	answer.dbo.DB = db
	return answer
}

func NewTXLevelDB(ldbpath string) (*TXDatabaseOverlay, error) {
	db, err := hybridDB.NewLevelMapHybridDB(ldbpath, false)
	if err != nil {
		fmt.Printf("err opening transaction db: %v\n", err)
	}

	if db == nil {
		fmt.Println("Creating new transaction db ...")
		db, err = hybridDB.NewLevelMapHybridDB(ldbpath, true)

		if err != nil {
			return nil, err
		}
	}
	fmt.Println("Transaction database started from: " + ldbpath)
	return NewTXOverlay(db), nil
}

func NewTXBoltDB(boltPath string) (*TXDatabaseOverlay, error) {
	db := hybridDB.NewBoltMapHybridDB(nil, boltPath)

	fmt.Println("Database started from: " + boltPath)
	return NewTXOverlay(db), nil
}

func (db *TXDatabaseOverlay) Close() error {
	return db.dbo.Close()
}

// GetAllTXs returns a list of all transactions in the history of Factom. A
// local database is used to cache the factoid blocks.
func (db *TXDatabaseOverlay) GetAllTXs() ([]interfaces.ITransaction, error) {
	// update the database and get the newest fblock
	newest, err := db.update()
	if err != nil {
		return nil, err
	}
	fblock, err := db.GetFBlock(newest)
	if err != nil {
		return nil, err
	}
	txs := make([]interfaces.ITransaction, 0)

	for {
		// get all of the txs from the block
		height := fblock.GetDatabaseHeight()
		for _, tx := range fblock.GetTransactions() {
			ins, err := tx.TotalInputs()
			if err != nil {
				return nil, err
			}
			outs, err := tx.TotalOutputs()
			if err != nil {
				return nil, err
			}

			if ins != 0 || outs != 0 {
				tx.SetBlockHeight(height)
				txs = append(txs, tx)
			}
		}

		if pre := fblock.GetPrevKeyMR().String(); pre != factom.ZeroHash {
			// get the previous block
			fblock, err = db.GetFBlock(pre)
			if err != nil {
				return nil, err
			} else if fblock == nil {
				return nil, fmt.Errorf("Missing fblock in database: %s", pre)
			}
		} else {
			break
		}
	}

	return txs, nil
}

// GetTX gets a transaction by the transaction id
func (db *TXDatabaseOverlay) GetTX(txid string) (
	interfaces.ITransaction, error) {
	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if tx.GetSigHash().String() == txid {
			return tx, nil
		}
	}

	return nil, fmt.Errorf("Transaction not found")
}

// GetTXAddress returns a list of all transactions in the history of Factom that
// include a specific address.
func (db *TXDatabaseOverlay) GetTXAddress(adr string) (
	[]interfaces.ITransaction, error) {
	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		for _, in := range tx.GetInputs() {
			if primitives.ConvertFctAddressToUserStr(in.GetAddress()) == adr {
				txs = append(filtered, tx)
			}
		}
		for _, out := range tx.GetOutputs() {
			if primitives.ConvertFctAddressToUserStr(out.GetAddress()) == adr {
				txs = append(filtered, tx)
			}
		}
		for _, out := range tx.GetECOutputs() {
			if primitives.ConvertECAddressToUserStr(out.GetAddress()) == adr {
				txs = append(filtered, tx)
			}
		}
	}

	return filtered, nil
}

func (db *TXDatabaseOverlay) GetTXRange(start, end int) (
	[]interfaces.ITransaction, error) {
	if start < 0 || end < 0 {
		return nil, fmt.Errorf("Range cannot have negative numbers")
	}
	s, e := uint32(start), uint32(end)

	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if s <= tx.GetBlockHeight() && tx.GetBlockHeight() <= e {
			filtered = append(filtered, tx)
		}
	}

	return filtered, nil
}

// GetFBlock retrives a Factoid Block from Factom
func (db *TXDatabaseOverlay) GetFBlock(keymr string) (interfaces.IFBlock, error) {
	fblock := new(factoid.FBlock)
	data, err := db.dbo.Get(fblockDBPrefix, []byte(keymr), fblock)
	if err != nil {
		return nil, err
	}
	if data == nil {
		return nil, nil
	}
	return data.(interfaces.IFBlock), nil
}

func (db *TXDatabaseOverlay) InsertFBlock(fblock interfaces.IFBlock) error {
	fblockmr := []byte(fblock.GetKeyMR().String())

	batch := []interfaces.Record{}
	batch = append(batch, interfaces.Record{fblockDBPrefix, fblockmr, fblock})

	return db.dbo.PutInBatch(batch)
}

// update gets all fblocks written since the database was last updated, and
// returns the most recent fblock keymr.
func (db *TXDatabaseOverlay) update() (string, error) {
	fblock, err := fblockHead()
	if err != nil {
		return "", err
	}
	newest := fblock.GetKeyMR().String()

	prevmr := fblock.GetPrevKeyMR().String()
	for prevmr != factom.ZeroHash {
		//		// stop when we reach an fblock that is already in the db
		//		if f, err := db.GetFBlock(prevmr); err != nil {
		//			return "", err
		//		} else if f != nil {
		//			return newest, nil
		//		}

		// add the fblock to the db
		if err := db.InsertFBlock(fblock); err != nil {
			return "", err
		}

		fblock, err = getfblock(prevmr)
		if err != nil {
			return "", err
		}
		prevmr = fblock.GetPrevKeyMR().String()
	}

	// write the last fblock into the db
	if err := db.InsertFBlock(fblock); err != nil {
		return "", err
	}

	return newest, nil
}

// fblockHead gets the most recent fblock.
func fblockHead() (interfaces.IFBlock, error) {
	fblockID := "000000000000000000000000000000000000000000000000000000000000000f"

	dbhead, err := factom.GetDBlockHead()
	if err != nil {
		return nil, err
	}
	dblock, err := factom.GetDBlock(dbhead)
	if err != nil {
		return nil, err
	}

	var fblockmr string
	for _, eblock := range dblock.EntryBlockList {
		if eblock.ChainID == fblockID {
			fblockmr = eblock.KeyMR
		}
	}
	if fblockmr == "" {
		return nil, err
	}

	return getfblock(fblockmr)
}

func getfblock(keymr string) (interfaces.IFBlock, error) {
	p, err := factom.GetRaw(keymr)
	if err != nil {
		return nil, err
	}
	return factoid.UnmarshalFBlock(p)
}
<|endoftext|>


<commit_before>// Hammer away at the chat server with fake connections
package main

import (
	"code.google.com/p/go-uuid/uuid"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"runtime"
	"time"
)

const msgurl = "http://127.0.0.1:8080/cmd"
const pollurl = "http://127.0.0.1:8080/poll"

var connectionCount int
var minDelay int32
var maxDelay int32

// Goroutine to run a long poll thread for a robot
func robotPoller(id string) {
	var payload interface{}

	transport := new(http.Transport)
	client := &http.Client{Transport: transport}

	for {
		// send in a poll request and wait for response
		resp, err := client.PostForm(pollurl,
			url.Values{"id": {id}})

		if err != nil {
			log.Printf("poller post error: %v", err)
			return
		}

		//log.Printf("************** %v %v", resp, err)
		body, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()

		if err != nil {
			log.Printf("http poll request error: %v", err)
			return
		}

		// extract payload
		err = json.Unmarshal(body, &payload)

		if err != nil {
			log.Printf("json poll unmarshal error: %v", err)
			log.Printf("json poll data: %s", string(body))
			return
		}

		outerObj := payload.([]interface{})

		for _, v := range outerObj {
			message := v.(map[string]interface{})

			respType := message["type"].(string)

			// check for login errors
			if respType == "status" {
				status := message["status"].(string)
				if status == "error" {
					message := message["message"].(string)
					log.Printf("chat server pollrequest error: %s", message)
					return
				}
			}

			// parse message
			switch respType {
			case "message":
				//log.Printf("%s: message: %s: %s", id, message["username"].(string), message["message"].(string))

			case "newuser":
				//log.Printf("%s: new user: %s", id, message["username"].(string))

			default:
				log.Printf("%s: unknown type: %s", id, respType)
			}
		}

		transport.CloseIdleConnections()
	}
}

// Goroutine to run a robot
func robot() {
	var payload interface{}

	transport := new(http.Transport)
	client := &http.Client{Transport: transport}

	id := uuid.New()

	// login
	resp, err := client.PostForm(msgurl,
		url.Values{"command": {"login"}, "id": {id}})

	if err != nil {
		log.Printf("http login error: %v", err)
		return
	}

	body, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()

	if err != nil {
		log.Printf("http login error: %v", err)
		return
	}

	// extract payload
	err = json.Unmarshal(body, &payload)

	if err != nil {
		log.Printf("json unmarshal error: %v", err)
		log.Printf("json data: %s", string(body))
		return
	}

	outerObj := payload.(map[string]interface{})
	respType := outerObj["type"].(string)

	// check for login errors
	if respType == "status" {
		status := outerObj["status"].(string)
		if status == "error" {
			message := outerObj["message"].(string)
			log.Printf("chat server login error: %s", message)
			return
		}
	}

	// get additional user data
	//publicId := outerObj["publicid"].(string)
	//userName := outerObj["username"].(string)

	// run the poller
	go robotPoller(id)

	msgCount := 0

	// main loop
	for {
		// send chat
		resp, err = client.PostForm(msgurl,
			url.Values{
				"command": {"broadcast"},
				"id":      {id},
				"message": {fmt.Sprintf("message %d", msgCount)},
			})

		msgCount++

		if err != nil {
			log.Printf("http broadcast error: %v", err)
			return
		}

		body, err = ioutil.ReadAll(resp.Body)
		resp.Body.Close()

		// check for broadcast errors
		if respType == "status" {
			status := outerObj["status"].(string)
			if status == "error" {
				message := outerObj["message"].(string)
				log.Printf("chat server broadcast error: %s", message)
				return
			}
		}

		// sleep a bit
		time.Sleep(time.Duration((rand.Int31n(maxDelay-minDelay) + minDelay)) * time.Millisecond)

		transport.CloseIdleConnections()
	}
}

// Main
func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	/*
		connectionCount = 200
		minDelay = 5000 // ms
		maxDelay = 20000
	*/
	connectionCount = 200
	minDelay = 500 // ms
	maxDelay = 1500

	for i := 0; i < connectionCount; i++ {
		go robot()
	}

	fmt.Printf("Hit return to quit")
	fmt.Scanln()
}
<commit_msg>Lower defaults so local sockets aren't overloaded<commit_after>// Hammer away at the chat server with fake connections
package main

import (
	"code.google.com/p/go-uuid/uuid"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"runtime"
	"time"
)

const msgurl = "http://127.0.0.1:8080/cmd"
const pollurl = "http://127.0.0.1:8080/poll"

var connectionCount int
var minDelay int32
var maxDelay int32

// Goroutine to run a long poll thread for a robot
func robotPoller(id string) {
	var payload interface{}

	transport := new(http.Transport)
	transport.DisableKeepAlives = true
	client := &http.Client{Transport: transport}

	for {
		// send in a poll request and wait for response
		resp, err := client.PostForm(pollurl,
			url.Values{"id": {id}})

		if err != nil {
			log.Printf("poller post error: %v", err)
			return
		}

		//log.Printf("************** %v %v", resp, err)
		body, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()

		if err != nil {
			log.Printf("http poll request error: %v", err)
			return
		}

		// extract payload
		err = json.Unmarshal(body, &payload)

		if err != nil {
			log.Printf("json poll unmarshal error: %v", err)
			log.Printf("json poll data: %s", string(body))
			return
		}

		outerObj := payload.([]interface{})

		for _, v := range outerObj {
			message := v.(map[string]interface{})

			respType := message["type"].(string)

			// check for login errors
			if respType == "status" {
				status := message["status"].(string)
				if status == "error" {
					message := message["message"].(string)
					log.Printf("chat server pollrequest error: %s", message)
					return
				}
			}

			// parse message
			switch respType {
			case "message":
				//log.Printf("%s: message: %s: %s", id, message["username"].(string), message["message"].(string))

			case "newuser":
				//log.Printf("%s: new user: %s", id, message["username"].(string))

			default:
				log.Printf("%s: unknown type: %s", id, respType)
			}
		}

		transport.CloseIdleConnections()
	}
}

// Goroutine to run a robot
func robot() {
	var payload interface{}

	transport := new(http.Transport)
	transport.DisableKeepAlives = true
	client := &http.Client{Transport: transport}

	id := uuid.New()

	// login
	resp, err := client.PostForm(msgurl,
		url.Values{"command": {"login"}, "id": {id}})

	if err != nil {
		log.Printf("http login error: %v", err)
		return
	}

	body, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()

	if err != nil {
		log.Printf("http login error: %v", err)
		return
	}

	// extract payload
	err = json.Unmarshal(body, &payload)

	if err != nil {
		log.Printf("json unmarshal error: %v", err)
		log.Printf("json data: %s", string(body))
		return
	}

	outerObj := payload.(map[string]interface{})
	respType := outerObj["type"].(string)

	// check for login errors
	if respType == "status" {
		status := outerObj["status"].(string)
		if status == "error" {
			message := outerObj["message"].(string)
			log.Printf("chat server login error: %s", message)
			return
		}
	}

	// get additional user data
	//publicId := outerObj["publicid"].(string)
	//userName := outerObj["username"].(string)

	// run the poller
	go robotPoller(id)

	msgCount := 0

	// main loop
	for {
		// send chat
		resp, err = client.PostForm(msgurl,
			url.Values{
				"command": {"broadcast"},
				"id":      {id},
				"message": {fmt.Sprintf("message %d", msgCount)},
			})

		msgCount++

		if err != nil {
			log.Printf("http broadcast error: %v", err)
			return
		}

		body, err = ioutil.ReadAll(resp.Body)
		resp.Body.Close()

		// check for broadcast errors
		if respType == "status" {
			status := outerObj["status"].(string)
			if status == "error" {
				message := outerObj["message"].(string)
				log.Printf("chat server broadcast error: %s", message)
				return
			}
		}

		// sleep a bit
		time.Sleep(time.Duration((rand.Int31n(maxDelay-minDelay) + minDelay)) * time.Millisecond)

		transport.CloseIdleConnections()
	}
}

// Main
func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	/*
		connectionCount = 200
		minDelay = 5000 // ms
		maxDelay = 20000
	*/
	connectionCount = 20
	minDelay = 1500 // ms
	maxDelay = 4000

	for i := 0; i < connectionCount; i++ {
		go robot()
	}

	fmt.Printf("Hit return to quit")
	fmt.Scanln()
}
<|endoftext|>


<commit_before>package reports

import (
	"strings"

	"github.com/ion-channel/ionic/aliases"
	"github.com/ion-channel/ionic/analysis"
	"github.com/ion-channel/ionic/projects"
	"github.com/ion-channel/ionic/rulesets"
	"github.com/ion-channel/ionic/scans"
	"github.com/ion-channel/ionic/tags"
)

// AnalysisReport is a Ion Channel representation of a report output from a
// given analysis
type AnalysisReport struct {
	*analysis.Analysis
	RulesetName   string             `json:"ruleset_name" xml:"ruleset_name"`
	Passed        bool               `json:"passed" xml:"passed"`
	Aliases       []aliases.Alias    `json:"aliases"`
	Tags          []tags.Tag         `json:"tags"`
	Trigger       string             `json:"trigger" xml:"trigger"`
	Risk          string             `json:"risk" xml:"risk"`
	Summary       string             `json:"summary" xml:"summary"`
	ScanSummaries []scans.Evaluation `json:"scan_summaries" xml:"scan_summaries"`
}

// NewAnalysisReport takes an Analysis and returns an initialized AnalysisReport
func NewAnalysisReport(analysis *analysis.Analysis, project *projects.Project, appliedRuleset *rulesets.AppliedRulesetSummary) (*AnalysisReport, error) {
	ar := AnalysisReport{
		Analysis: analysis,
		Trigger:  "source commit",
		Risk:     "high",
	}

	// Project Details
	ar.Aliases = project.Aliases
	ar.Tags = project.Tags

	// RulesetEval Details
	if appliedRuleset != nil && appliedRuleset.RuleEvaluationSummary != nil {
		ar.RulesetName = appliedRuleset.RuleEvaluationSummary.RulesetName

		if strings.ToLower(appliedRuleset.RuleEvaluationSummary.Summary) == "pass" {
			ar.Risk = "low"
			ar.Passed = true
		}

		for i := range appliedRuleset.RuleEvaluationSummary.Ruleresults {
			appliedRuleset.RuleEvaluationSummary.Ruleresults[i].Translate()
		}

		ar.ScanSummaries = appliedRuleset.RuleEvaluationSummary.Ruleresults
	}

	return &ar, nil
}
<commit_msg>doubling up field to migrate when UI is ready<commit_after>package reports

import (
	"strings"

	"github.com/ion-channel/ionic/aliases"
	"github.com/ion-channel/ionic/analysis"
	"github.com/ion-channel/ionic/projects"
	"github.com/ion-channel/ionic/rulesets"
	"github.com/ion-channel/ionic/scans"
	"github.com/ion-channel/ionic/tags"
)

// AnalysisReport is a Ion Channel representation of a report output from a
// given analysis
type AnalysisReport struct {
	*analysis.Analysis
	RulesetName   string             `json:"ruleset_name" xml:"ruleset_name"`
	Passed        bool               `json:"passed" xml:"passed"`
	Aliases       []aliases.Alias    `json:"aliases"`
	Tags          []tags.Tag         `json:"tags"`
	Trigger       string             `json:"trigger" xml:"trigger"`
	Risk          string             `json:"risk" xml:"risk"`
	Summary       string             `json:"summary" xml:"summary"`
	ScanSummaries []scans.Evaluation `json:"scan_summaries" xml:"scan_summaries"`
	Evaluations   []scans.Evaluation `json:"evaluations" xml:"evaluations"`
}

// NewAnalysisReport takes an Analysis and returns an initialized AnalysisReport
func NewAnalysisReport(analysis *analysis.Analysis, project *projects.Project, appliedRuleset *rulesets.AppliedRulesetSummary) (*AnalysisReport, error) {
	ar := AnalysisReport{
		Analysis: analysis,
		Trigger:  "source commit",
		Risk:     "high",
	}

	// Project Details
	ar.Aliases = project.Aliases
	ar.Tags = project.Tags

	// RulesetEval Details
	if appliedRuleset != nil && appliedRuleset.RuleEvaluationSummary != nil {
		ar.RulesetName = appliedRuleset.RuleEvaluationSummary.RulesetName

		if strings.ToLower(appliedRuleset.RuleEvaluationSummary.Summary) == "pass" {
			ar.Risk = "low"
			ar.Passed = true
		}

		for i := range appliedRuleset.RuleEvaluationSummary.Ruleresults {
			appliedRuleset.RuleEvaluationSummary.Ruleresults[i].Translate()
		}

		// TODO: Remove ScanSummaries field
		ar.ScanSummaries = appliedRuleset.RuleEvaluationSummary.Ruleresults
		ar.Evaluations = appliedRuleset.RuleEvaluationSummary.Ruleresults
	}

	return &ar, nil
}
<|endoftext|>


<commit_before>package torrent

import (
	"sort"
	"time"
	"unsafe"

	"github.com/anacrolix/multiless"
	pp "github.com/anacrolix/torrent/peer_protocol"
	"github.com/bradfitz/iter"
)

type clientPieceRequestOrder struct {
	pieces []pieceRequestOrderPiece
}

type pieceRequestOrderPiece struct {
	t            *Torrent
	index        pieceIndex
	prio         piecePriority
	partial      bool
	availability int64
	request      bool
}

func (me *clientPieceRequestOrder) Len() int {
	return len(me.pieces)
}

func (me clientPieceRequestOrder) sort() {
	sort.Slice(me.pieces, me.less)
}

func (me clientPieceRequestOrder) less(_i, _j int) bool {
	i := me.pieces[_i]
	j := me.pieces[_j]
	return multiless.New().Int(
		int(j.prio), int(i.prio),
	).Bool(
		j.partial, i.partial,
	).Int64(i.availability, j.availability).Int(i.index, j.index).Less()
}

func (cl *Client) requester() {
	for {
		func() {
			cl.lock()
			defer cl.unlock()
			cl.doRequests()
		}()
		select {
		case <-cl.closed.LockedChan(cl.locker()):
			return
		case <-time.After(100 * time.Millisecond):
		}
	}
}

type requestsPeer struct {
	cur                        *Peer
	nextRequests               map[Request]struct{}
	nextInterest               bool
	requestablePiecesRemaining int
}

func (rp *requestsPeer) canRequestPiece(p pieceIndex) bool {
	return rp.hasPiece(p) && (!rp.choking() || rp.pieceAllowedFast(p))
}

func (rp *requestsPeer) hasPiece(i pieceIndex) bool {
	return rp.cur.peerHasPiece(i)
}

func (rp *requestsPeer) pieceAllowedFast(p pieceIndex) bool {
	return rp.cur.peerAllowedFast.Contains(p)
}

func (rp *requestsPeer) choking() bool {
	return rp.cur.peerChoking
}

func (rp *requestsPeer) hasExistingRequest(r Request) bool {
	_, ok := rp.cur.requests[r]
	return ok
}

func (rp *requestsPeer) canFitRequest() bool {
	return len(rp.nextRequests) < rp.cur.nominalMaxRequests()
}

// Returns true if it is added and wasn't there before.
func (rp *requestsPeer) addNextRequest(r Request) bool {
	_, ok := rp.nextRequests[r]
	if ok {
		return false
	}
	rp.nextRequests[r] = struct{}{}
	return true
}

type peersForPieceRequests struct {
	requestsInPiece int
	*requestsPeer
}

func (me *peersForPieceRequests) addNextRequest(r Request) {
	if me.requestsPeer.addNextRequest(r) {
		return
		me.requestsInPiece++
	}
}

func (cl *Client) doRequests() {
	requestOrder := &cl.pieceRequestOrder
	requestOrder.pieces = requestOrder.pieces[:0]
	allPeers := make(map[*Torrent][]*requestsPeer)
	// Storage capacity left for this run, keyed by the storage capacity pointer on the storage
	// TorrentImpl.
	storageLeft := make(map[*func() *int64]*int64)
	for _, t := range cl.torrents {
		// TODO: We could do metainfo requests here.
		if !t.haveInfo() {
			continue
		}
		key := t.storage.Capacity
		if key != nil {
			if _, ok := storageLeft[key]; !ok {
				storageLeft[key] = (*key)()
			}
		}
		var peers []*requestsPeer
		t.iterPeers(func(p *Peer) {
			if !p.closed.IsSet() {
				peers = append(peers, &requestsPeer{
					cur:          p,
					nextRequests: make(map[Request]struct{}),
				})
			}
		})
		for i := range iter.N(t.numPieces()) {
			tp := t.piece(i)
			pp := tp.purePriority()
			request := !t.ignorePieceForRequests(i)
			requestOrder.pieces = append(requestOrder.pieces, pieceRequestOrderPiece{
				t:            t,
				index:        i,
				prio:         pp,
				partial:      t.piecePartiallyDownloaded(i),
				availability: tp.availability,
				request:      request,
			})
			if request {
				for _, p := range peers {
					if p.canRequestPiece(i) {
						p.requestablePiecesRemaining++
					}
				}
			}
		}
		allPeers[t] = peers
	}
	requestOrder.sort()
	for _, p := range requestOrder.pieces {
		torrentPiece := p.t.piece(p.index)
		if left := storageLeft[p.t.storage.Capacity]; left != nil {
			if *left < int64(torrentPiece.length()) {
				continue
			}
			*left -= int64(torrentPiece.length())
		}
		if !p.request {
			continue
		}
		peersForPiece := make([]*peersForPieceRequests, 0, len(allPeers[p.t]))
		for _, peer := range allPeers[p.t] {
			peersForPiece = append(peersForPiece, &peersForPieceRequests{
				requestsInPiece: 0,
				requestsPeer:    peer,
			})
		}
		sortPeersForPiece := func() {
			sort.Slice(peersForPiece, func(i, j int) bool {
				return multiless.New().Bool(
					peersForPiece[j].canFitRequest(),
					peersForPiece[i].canFitRequest(),
				).Int(
					peersForPiece[i].requestsInPiece,
					peersForPiece[j].requestsInPiece,
				).Int(
					peersForPiece[i].requestablePiecesRemaining,
					peersForPiece[j].requestablePiecesRemaining,
				).Float64(
					peersForPiece[j].cur.downloadRate(),
					peersForPiece[i].cur.downloadRate(),
				).EagerSameLess(
					peersForPiece[i].cur.completedHandshake.Equal(peersForPiece[j].cur.completedHandshake),
					peersForPiece[i].cur.completedHandshake.Before(peersForPiece[j].cur.completedHandshake),
					// TODO: Probably peer priority can come next
				).Uintptr(
					uintptr(unsafe.Pointer(peersForPiece[j].cur)),
					uintptr(unsafe.Pointer(peersForPiece[i].cur)),
				).Less()
			})
		}
		pendingChunksRemaining := int(p.t.pieceNumPendingChunks(p.index))
		torrentPiece.iterUndirtiedChunks(func(chunk ChunkSpec) bool {
			req := Request{pp.Integer(p.index), chunk}
			pendingChunksRemaining--
			sortPeersForPiece()
			for i, peer := range peersForPiece {
				if i > pendingChunksRemaining {
					break
				}
				if peer.hasExistingRequest(req) && peer.canFitRequest() {
					peer.addNextRequest(req)
					return true
				}
			}
			for _, peer := range peersForPiece {
				if !peer.canFitRequest() {
					continue
				}
				if !peer.hasPiece(p.index) {
					continue
				}
				if !peer.pieceAllowedFast(p.index) {
					// TODO: Verify that's okay to stay uninterested if we request allowed fast
					// pieces.
					peer.nextInterest = true
					if peer.choking() {
						continue
					}
				}
				peer.addNextRequest(req)
				return true
			}
			return true
		})
		for _, peer := range peersForPiece {
			if peer.canRequestPiece(p.index) {
				peer.requestablePiecesRemaining--
			}
		}
	}
	for _, peers := range allPeers {
		for _, rp := range peers {
			if rp.requestablePiecesRemaining != 0 {
				panic(rp.requestablePiecesRemaining)
			}
			applyPeerNextRequests(rp)
		}
	}
}

func applyPeerNextRequests(rp *requestsPeer) {
	p := rp.cur
	p.setInterested(rp.nextInterest)
	for req := range p.requests {
		if _, ok := rp.nextRequests[req]; !ok {
			p.cancel(req)
		}
	}
	for req := range rp.nextRequests {
		err := p.request(req)
		if err != nil {
			panic(err)
		} else {
			//log.Print(req)
		}
	}
}
<commit_msg>Fixes to reassigning chunks in requests<commit_after>package torrent

import (
	"sort"
	"time"
	"unsafe"

	"github.com/anacrolix/multiless"
	pp "github.com/anacrolix/torrent/peer_protocol"
	"github.com/bradfitz/iter"
)

type clientPieceRequestOrder struct {
	pieces []pieceRequestOrderPiece
}

type pieceRequestOrderPiece struct {
	t            *Torrent
	index        pieceIndex
	prio         piecePriority
	partial      bool
	availability int64
	request      bool
}

func (me *clientPieceRequestOrder) Len() int {
	return len(me.pieces)
}

func (me clientPieceRequestOrder) sort() {
	sort.Slice(me.pieces, me.less)
}

func (me clientPieceRequestOrder) less(_i, _j int) bool {
	i := me.pieces[_i]
	j := me.pieces[_j]
	return multiless.New().Int(
		int(j.prio), int(i.prio),
	).Bool(
		j.partial, i.partial,
	).Int64(i.availability, j.availability).Int(i.index, j.index).Less()
}

func (cl *Client) requester() {
	for {
		func() {
			cl.lock()
			defer cl.unlock()
			cl.doRequests()
		}()
		select {
		case <-cl.closed.LockedChan(cl.locker()):
			return
		case <-time.After(100 * time.Millisecond):
		}
	}
}

type requestsPeer struct {
	cur                        *Peer
	nextRequests               map[Request]struct{}
	nextInterest               bool
	requestablePiecesRemaining int
}

func (rp *requestsPeer) canRequestPiece(p pieceIndex) bool {
	return rp.hasPiece(p) && (!rp.choking() || rp.pieceAllowedFast(p))
}

func (rp *requestsPeer) hasPiece(i pieceIndex) bool {
	return rp.cur.peerHasPiece(i)
}

func (rp *requestsPeer) pieceAllowedFast(p pieceIndex) bool {
	return rp.cur.peerAllowedFast.Contains(p)
}

func (rp *requestsPeer) choking() bool {
	return rp.cur.peerChoking
}

func (rp *requestsPeer) hasExistingRequest(r Request) bool {
	_, ok := rp.cur.requests[r]
	return ok
}

func (rp *requestsPeer) canFitRequest() bool {
	return len(rp.nextRequests) < rp.cur.nominalMaxRequests()
}

// Returns true if it is added and wasn't there before.
func (rp *requestsPeer) addNextRequest(r Request) bool {
	_, ok := rp.nextRequests[r]
	if ok {
		return false
	}
	rp.nextRequests[r] = struct{}{}
	return true
}

type peersForPieceRequests struct {
	requestsInPiece int
	*requestsPeer
}

func (me *peersForPieceRequests) addNextRequest(r Request) {
	if me.requestsPeer.addNextRequest(r) {
		return
		me.requestsInPiece++
	}
}

func (cl *Client) doRequests() {
	requestOrder := &cl.pieceRequestOrder
	requestOrder.pieces = requestOrder.pieces[:0]
	allPeers := make(map[*Torrent][]*requestsPeer)
	// Storage capacity left for this run, keyed by the storage capacity pointer on the storage
	// TorrentImpl.
	storageLeft := make(map[*func() *int64]*int64)
	for _, t := range cl.torrents {
		// TODO: We could do metainfo requests here.
		if !t.haveInfo() {
			continue
		}
		key := t.storage.Capacity
		if key != nil {
			if _, ok := storageLeft[key]; !ok {
				storageLeft[key] = (*key)()
			}
		}
		var peers []*requestsPeer
		t.iterPeers(func(p *Peer) {
			if !p.closed.IsSet() {
				peers = append(peers, &requestsPeer{
					cur:          p,
					nextRequests: make(map[Request]struct{}),
				})
			}
		})
		for i := range iter.N(t.numPieces()) {
			tp := t.piece(i)
			pp := tp.purePriority()
			request := !t.ignorePieceForRequests(i)
			requestOrder.pieces = append(requestOrder.pieces, pieceRequestOrderPiece{
				t:            t,
				index:        i,
				prio:         pp,
				partial:      t.piecePartiallyDownloaded(i),
				availability: tp.availability,
				request:      request,
			})
			if request {
				for _, p := range peers {
					if p.canRequestPiece(i) {
						p.requestablePiecesRemaining++
					}
				}
			}
		}
		allPeers[t] = peers
	}
	requestOrder.sort()
	for _, p := range requestOrder.pieces {
		torrentPiece := p.t.piece(p.index)
		if left := storageLeft[p.t.storage.Capacity]; left != nil {
			if *left < int64(torrentPiece.length()) {
				continue
			}
			*left -= int64(torrentPiece.length())
		}
		if !p.request {
			continue
		}
		peersForPiece := make([]*peersForPieceRequests, 0, len(allPeers[p.t]))
		for _, peer := range allPeers[p.t] {
			peersForPiece = append(peersForPiece, &peersForPieceRequests{
				requestsInPiece: 0,
				requestsPeer:    peer,
			})
		}
		sortPeersForPiece := func() {
			sort.Slice(peersForPiece, func(i, j int) bool {
				return multiless.New().Int(
					peersForPiece[i].requestsInPiece,
					peersForPiece[j].requestsInPiece,
				).Int(
					peersForPiece[i].requestablePiecesRemaining,
					peersForPiece[j].requestablePiecesRemaining,
				).Float64(
					peersForPiece[j].cur.downloadRate(),
					peersForPiece[i].cur.downloadRate(),
				).EagerSameLess(
					peersForPiece[i].cur.completedHandshake.Equal(peersForPiece[j].cur.completedHandshake),
					peersForPiece[i].cur.completedHandshake.Before(peersForPiece[j].cur.completedHandshake),
					// TODO: Probably peer priority can come next
				).Uintptr(
					uintptr(unsafe.Pointer(peersForPiece[j].cur)),
					uintptr(unsafe.Pointer(peersForPiece[i].cur)),
				).Less()
			})
		}
		pendingChunksRemaining := int(p.t.pieceNumPendingChunks(p.index))
		torrentPiece.iterUndirtiedChunks(func(chunk ChunkSpec) bool {
			req := Request{pp.Integer(p.index), chunk}
			pendingChunksRemaining--
			sortPeersForPiece()
			skipped := 0
			// Try up to the number of peers that could legitimately receive the request equal to
			// the number of chunks left. This should ensure that only the best peers serve the last
			// few chunks in a piece.
			for _, peer := range peersForPiece {
				if !peer.canFitRequest() || !peer.hasPiece(p.index) || (!peer.pieceAllowedFast(p.index) && peer.choking()) {
					continue
				}
				if skipped > pendingChunksRemaining {
					break
				}
				if !peer.hasExistingRequest(req) {
					skipped++
					continue
				}
				if !peer.pieceAllowedFast(p.index) {
					// We must stay interested for this.
					peer.nextInterest = true
				}
				peer.addNextRequest(req)
				return true
			}
			for _, peer := range peersForPiece {
				if !peer.canFitRequest() {
					continue
				}
				if !peer.hasPiece(p.index) {
					continue
				}
				if !peer.pieceAllowedFast(p.index) {
					// TODO: Verify that's okay to stay uninterested if we request allowed fast
					// pieces.
					peer.nextInterest = true
					if peer.choking() {
						continue
					}
				}
				peer.addNextRequest(req)
				return true
			}
			return true
		})
		if pendingChunksRemaining != 0 {
			panic(pendingChunksRemaining)
		}
		for _, peer := range peersForPiece {
			if peer.canRequestPiece(p.index) {
				peer.requestablePiecesRemaining--
			}
		}
	}
	for _, peers := range allPeers {
		for _, rp := range peers {
			if rp.requestablePiecesRemaining != 0 {
				panic(rp.requestablePiecesRemaining)
			}
			applyPeerNextRequests(rp)
		}
	}
}

func applyPeerNextRequests(rp *requestsPeer) {
	p := rp.cur
	p.setInterested(rp.nextInterest)
	for req := range p.requests {
		if _, ok := rp.nextRequests[req]; !ok {
			p.cancel(req)
		}
	}
	for req := range rp.nextRequests {
		err := p.request(req)
		if err != nil {
			panic(err)
		} else {
			//log.Print(req)
		}
	}
}
<|endoftext|>


<commit_before>package filecache

import "container/list"

type Iterator interface {
	Next() Iterator
	Value() interface{}
}

type listElementIterator struct {
	le *list.Element
}

func (me listElementIterator) Next() Iterator {
	e := me.le.Next()
	if e == nil {
		return nil
	}
	return listElementIterator{e}
}

func (me listElementIterator) Value() interface{} {
	return me.le.Value
}

func newLRUItems() *lruItems {
	return &lruItems{list.New(), make(map[ItemInfo]*list.Element)}
}

type lruItems struct {
	list  *list.List
	elems map[ItemInfo]*list.Element
}

func (me *lruItems) Front() Iterator {
	e := me.list.Front()
	if e == nil {
		return nil
	}
	return listElementIterator{e}
}

func (me *lruItems) LRU() ItemInfo {
	return me.list.Front().Value.(ItemInfo)
}

func (me *lruItems) insertList(ii ItemInfo) *list.Element {
	for e := me.list.Back(); e != nil; e = e.Prev() {
		if ii.Accessed.After(e.Value.(ItemInfo).Accessed) {
			return me.list.InsertAfter(ii, e)
		}
	}
	return me.list.PushFront(ii)
}

func (me *lruItems) Insert(ii ItemInfo) {
	le := me.insertList(ii)
	me.elems[ii] = le
}

func (me *lruItems) Remove(ii ItemInfo) bool {
	e, ok := me.elems[ii]
	if !ok {
		return false
	}
	delete(me.elems, ii)
	if me.list.Remove(e).(ItemInfo) != ii {
		panic("f7u12")
	}
	return true
}
<commit_msg>filecache: Store LRU items in a tree<commit_after>package filecache

import (
	"container/list"
	"io"

	"github.com/cznic/b"
)

type Iterator interface {
	Next() Iterator
	Value() interface{}
}

type listElementIterator struct {
	le *list.Element
}

func (me listElementIterator) Next() Iterator {
	e := me.le.Next()
	if e == nil {
		return nil
	}
	return listElementIterator{e}
}

func (me listElementIterator) Value() interface{} {
	return me.le.Value
}

func newLRUItems() *lruItems {
	return &lruItems{b.TreeNew(func(_a, _b interface{}) int {
		a := _a.(ItemInfo)
		b := _b.(ItemInfo)
		if a.Accessed != b.Accessed {
			if a.Accessed.Before(b.Accessed) {
				return -1
			} else {
				return 1
			}
		}
		if a.Path == b.Path {
			return 0
		}
		if a.Path < b.Path {
			return -1
		}
		return 1
	})}
}

type lruItems struct {
	tree *b.Tree
}

type bEnumeratorIterator struct {
	e *b.Enumerator
	v ItemInfo
}

func (me bEnumeratorIterator) Next() Iterator {
	_, v, err := me.e.Next()
	if err == io.EOF {
		return nil
	}
	return bEnumeratorIterator{me.e, v.(ItemInfo)}
}

func (me bEnumeratorIterator) Value() interface{} {
	return me.v
}

func (me *lruItems) Front() Iterator {
	e, _ := me.tree.SeekFirst()
	if e == nil {
		return nil
	}
	return bEnumeratorIterator{
		e: e,
	}.Next()
}

func (me *lruItems) LRU() ItemInfo {
	_, v := me.tree.First()
	return v.(ItemInfo)
}

func (me *lruItems) Insert(ii ItemInfo) {
	me.tree.Set(ii, ii)
}

func (me *lruItems) Remove(ii ItemInfo) bool {
	return me.tree.Delete(ii)
}
<|endoftext|>


<commit_before>package flatfs_test

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/jbenet/go-datastore"
	"github.com/jbenet/go-datastore/flatfs"
	"github.com/jbenet/go-datastore/query"
)

func tempdir(t testing.TB) (path string, cleanup func()) {
	path, err := ioutil.TempDir("", "test-datastore-flatfs-")
	if err != nil {
		t.Fatalf("cannot create temp directory: %v", err)
	}

	cleanup = func() {
		if err := os.RemoveAll(path); err != nil {
			t.Errorf("tempdir cleanup failed: %v", err)
		}
	}
	return path, cleanup
}

func TestBadPrefixLen(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	for i := 0; i > -3; i-- {
		_, err := flatfs.New(temp, 0)
		if g, e := err, flatfs.ErrBadPrefixLen; g != e {
			t.Errorf("expected ErrBadPrefixLen, got: %v", g)
		}
	}
}

func TestPutBadValueType(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	err = fs.Put(datastore.NewKey("quux"), 22)
	if g, e := err, datastore.ErrInvalidType; g != e {
		t.Fatalf("expected ErrInvalidType, got: %v\n", g)
	}
}

func TestPut(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	err = fs.Put(datastore.NewKey("quux"), []byte("foobar"))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}
}

func TestGet(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	const input = "foobar"
	err = fs.Put(datastore.NewKey("quux"), []byte(input))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}

	data, err := fs.Get(datastore.NewKey("quux"))
	if err != nil {
		t.Fatalf("Get failed: %v", err)
	}
	buf, ok := data.([]byte)
	if !ok {
		t.Fatalf("expected []byte from Get, got %T: %v", data, data)
	}
	if g, e := string(buf), input; g != e {
		t.Fatalf("Get gave wrong content: %q != %q", g, e)
	}
}

func TestPutOverwrite(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	const (
		loser  = "foobar"
		winner = "xyzzy"
	)
	err = fs.Put(datastore.NewKey("quux"), []byte(loser))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}

	err = fs.Put(datastore.NewKey("quux"), []byte(winner))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}

	data, err := fs.Get(datastore.NewKey("quux"))
	if err != nil {
		t.Fatalf("Get failed: %v", err)
	}
	if g, e := string(data.([]byte)), winner; g != e {
		t.Fatalf("Get gave wrong content: %q != %q", g, e)
	}
}

func TestGetNotFoundError(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	_, err = fs.Get(datastore.NewKey("quux"))
	if g, e := err, datastore.ErrNotFound; g != e {
		t.Fatalf("expected ErrNotFound, got: %v\n", g)
	}
}

func TestStorage(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	const prefixLen = 2
	const prefix = "2f71"
	const target = prefix + "/2f71757578.data"
	fs, err := flatfs.New(temp, prefixLen)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	err = fs.Put(datastore.NewKey("quux"), []byte("foobar"))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}

	seen := false
	walk := func(absPath string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		path, err := filepath.Rel(temp, absPath)
		if err != nil {
			return err
		}
		switch path {
		case ".", "..":
			// ignore
		case prefix:
			if !fi.IsDir() {
				t.Errorf("prefix directory is not a file? %v", fi.Mode())
			}
			// we know it's there if we see the file, nothing more to
			// do here
		case target:
			seen = true
			if !fi.Mode().IsRegular() {
				t.Errorf("expected a regular file, mode: %04o", fi.Mode())
			}
			if g, e := fi.Mode()&os.ModePerm&0007, os.FileMode(0000); g != e {
				t.Errorf("file should not be world accessible: %04o", fi.Mode())
			}
		default:
			t.Errorf("saw unexpected directory entry: %q %v", path, fi.Mode())
		}
		return nil
	}
	if err := filepath.Walk(temp, walk); err != nil {
		t.Fatal("walk: %v", err)
	}
	if !seen {
		t.Error("did not see the data file")
	}
}

func TestHasNotFound(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	found, err := fs.Has(datastore.NewKey("quux"))
	if err != nil {
		t.Fatalf("Has fail: %v\n", err)
	}
	if g, e := found, false; g != e {
		t.Fatalf("wrong Has: %v != %v", g, e)
	}
}

func TestHasFound(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}
	err = fs.Put(datastore.NewKey("quux"), []byte("foobar"))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}

	found, err := fs.Has(datastore.NewKey("quux"))
	if err != nil {
		t.Fatalf("Has fail: %v\n", err)
	}
	if g, e := found, true; g != e {
		t.Fatalf("wrong Has: %v != %v", g, e)
	}
}

func TestDeleteNotFound(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	err = fs.Delete(datastore.NewKey("quux"))
	if g, e := err, datastore.ErrNotFound; g != e {
		t.Fatalf("expected ErrNotFound, got: %v\n", g)
	}
}

func TestDeleteFound(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}
	err = fs.Put(datastore.NewKey("quux"), []byte("foobar"))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}

	err = fs.Delete(datastore.NewKey("quux"))
	if err != nil {
		t.Fatalf("Delete fail: %v\n", err)
	}

	// check that it's gone
	_, err = fs.Get(datastore.NewKey("quux"))
	if g, e := err, datastore.ErrNotFound; g != e {
		t.Fatalf("expected Get after Delete to give ErrNotFound, got: %v\n", g)
	}
}

func TestQuerySimple(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}
	const myKey = "quux"
	err = fs.Put(datastore.NewKey(myKey), []byte("foobar"))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}

	res, err := fs.Query(query.Query{KeysOnly: true})
	if err != nil {
		t.Fatalf("Query fail: %v\n", err)
	}
	entries, err := res.Rest()
	if err != nil {
		t.Fatalf("Query Results.Rest fail: %v\n", err)
	}
	seen := false
	for _, e := range entries {
		switch e.Key {
		case datastore.NewKey(myKey).String():
			seen = true
		default:
			t.Errorf("saw unexpected key: %q", e.Key)
		}
	}
	if !seen {
		t.Errorf("did not see wanted key %q in %+v", myKey, entries)
	}
}
<commit_msg>flatfs: Fix tests after previous commit<commit_after>package flatfs_test

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/jbenet/go-datastore"
	"github.com/jbenet/go-datastore/flatfs"
	"github.com/jbenet/go-datastore/query"
)

func tempdir(t testing.TB) (path string, cleanup func()) {
	path, err := ioutil.TempDir("", "test-datastore-flatfs-")
	if err != nil {
		t.Fatalf("cannot create temp directory: %v", err)
	}

	cleanup = func() {
		if err := os.RemoveAll(path); err != nil {
			t.Errorf("tempdir cleanup failed: %v", err)
		}
	}
	return path, cleanup
}

func TestBadPrefixLen(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	for i := 0; i > -3; i-- {
		_, err := flatfs.New(temp, 0)
		if g, e := err, flatfs.ErrBadPrefixLen; g != e {
			t.Errorf("expected ErrBadPrefixLen, got: %v", g)
		}
	}
}

func TestPutBadValueType(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	err = fs.Put(datastore.NewKey("quux"), 22)
	if g, e := err, datastore.ErrInvalidType; g != e {
		t.Fatalf("expected ErrInvalidType, got: %v\n", g)
	}
}

func TestPut(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	err = fs.Put(datastore.NewKey("quux"), []byte("foobar"))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}
}

func TestGet(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	const input = "foobar"
	err = fs.Put(datastore.NewKey("quux"), []byte(input))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}

	data, err := fs.Get(datastore.NewKey("quux"))
	if err != nil {
		t.Fatalf("Get failed: %v", err)
	}
	buf, ok := data.([]byte)
	if !ok {
		t.Fatalf("expected []byte from Get, got %T: %v", data, data)
	}
	if g, e := string(buf), input; g != e {
		t.Fatalf("Get gave wrong content: %q != %q", g, e)
	}
}

func TestPutOverwrite(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	const (
		loser  = "foobar"
		winner = "xyzzy"
	)
	err = fs.Put(datastore.NewKey("quux"), []byte(loser))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}

	err = fs.Put(datastore.NewKey("quux"), []byte(winner))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}

	data, err := fs.Get(datastore.NewKey("quux"))
	if err != nil {
		t.Fatalf("Get failed: %v", err)
	}
	if g, e := string(data.([]byte)), winner; g != e {
		t.Fatalf("Get gave wrong content: %q != %q", g, e)
	}
}

func TestGetNotFoundError(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	_, err = fs.Get(datastore.NewKey("quux"))
	if g, e := err, datastore.ErrNotFound; g != e {
		t.Fatalf("expected ErrNotFound, got: %v\n", g)
	}
}

func TestStorage(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	const prefixLen = 2
	const prefix = "7175"
	const target = prefix + "/71757578.data"
	fs, err := flatfs.New(temp, prefixLen)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	err = fs.Put(datastore.NewKey("quux"), []byte("foobar"))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}

	seen := false
	walk := func(absPath string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		path, err := filepath.Rel(temp, absPath)
		if err != nil {
			return err
		}
		switch path {
		case ".", "..":
			// ignore
		case prefix:
			if !fi.IsDir() {
				t.Errorf("prefix directory is not a file? %v", fi.Mode())
			}
			// we know it's there if we see the file, nothing more to
			// do here
		case target:
			seen = true
			if !fi.Mode().IsRegular() {
				t.Errorf("expected a regular file, mode: %04o", fi.Mode())
			}
			if g, e := fi.Mode()&os.ModePerm&0007, os.FileMode(0000); g != e {
				t.Errorf("file should not be world accessible: %04o", fi.Mode())
			}
		default:
			t.Errorf("saw unexpected directory entry: %q %v", path, fi.Mode())
		}
		return nil
	}
	if err := filepath.Walk(temp, walk); err != nil {
		t.Fatal("walk: %v", err)
	}
	if !seen {
		t.Error("did not see the data file")
	}
}

func TestHasNotFound(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	found, err := fs.Has(datastore.NewKey("quux"))
	if err != nil {
		t.Fatalf("Has fail: %v\n", err)
	}
	if g, e := found, false; g != e {
		t.Fatalf("wrong Has: %v != %v", g, e)
	}
}

func TestHasFound(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}
	err = fs.Put(datastore.NewKey("quux"), []byte("foobar"))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}

	found, err := fs.Has(datastore.NewKey("quux"))
	if err != nil {
		t.Fatalf("Has fail: %v\n", err)
	}
	if g, e := found, true; g != e {
		t.Fatalf("wrong Has: %v != %v", g, e)
	}
}

func TestDeleteNotFound(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}

	err = fs.Delete(datastore.NewKey("quux"))
	if g, e := err, datastore.ErrNotFound; g != e {
		t.Fatalf("expected ErrNotFound, got: %v\n", g)
	}
}

func TestDeleteFound(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}
	err = fs.Put(datastore.NewKey("quux"), []byte("foobar"))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}

	err = fs.Delete(datastore.NewKey("quux"))
	if err != nil {
		t.Fatalf("Delete fail: %v\n", err)
	}

	// check that it's gone
	_, err = fs.Get(datastore.NewKey("quux"))
	if g, e := err, datastore.ErrNotFound; g != e {
		t.Fatalf("expected Get after Delete to give ErrNotFound, got: %v\n", g)
	}
}

func TestQuerySimple(t *testing.T) {
	temp, cleanup := tempdir(t)
	defer cleanup()

	fs, err := flatfs.New(temp, 2)
	if err != nil {
		t.Fatalf("New fail: %v\n", err)
	}
	const myKey = "quux"
	err = fs.Put(datastore.NewKey(myKey), []byte("foobar"))
	if err != nil {
		t.Fatalf("Put fail: %v\n", err)
	}

	res, err := fs.Query(query.Query{KeysOnly: true})
	if err != nil {
		t.Fatalf("Query fail: %v\n", err)
	}
	entries, err := res.Rest()
	if err != nil {
		t.Fatalf("Query Results.Rest fail: %v\n", err)
	}
	seen := false
	for _, e := range entries {
		switch e.Key {
		case datastore.NewKey(myKey).String():
			seen = true
		default:
			t.Errorf("saw unexpected key: %q", e.Key)
		}
	}
	if !seen {
		t.Errorf("did not see wanted key %q in %+v", myKey, entries)
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"strconv"

	"github.com/hashicorp/terraform/flatmap"
	"github.com/hashicorp/terraform/helper/config"
	"github.com/hashicorp/terraform/helper/diff"
	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/goamz/elb"
)

func resource_aws_elb_create(
	s *terraform.ResourceState,
	d *terraform.ResourceDiff,
	meta interface{}) (*terraform.ResourceState, error) {
	p := meta.(*ResourceProvider)
	elbconn := p.elbconn

	// Merge the diff into the state so that we have all the attributes
	// properly.
	rs := s.MergeDiff(d)

	// The name specified for the ELB. This is also our unique ID
	// we save to state if the creation is successful (amazon verifies
	// it is unique)
	elbName := rs.Attributes["name"]

	// Expand the "listener" array to goamz compat []elb.Listener
	v := flatmap.Expand(rs.Attributes, "listener").([]interface{})
	listeners, err := expandListeners(v)
	if err != nil {
		return nil, err
	}

	// Provision the elb
	elbOpts := &elb.CreateLoadBalancer{
		LoadBalancerName: elbName,
		Listeners:        listeners,
	}

	if _, ok := rs.Attributes["availability_zones.#"]; ok {
		v = flatmap.Expand(rs.Attributes, "availability_zones").([]interface{})
		elbOpts.AvailZone = expandStringList(v)
	}

	if _, ok := rs.Attributes["security_groups.#"]; ok {
		v = flatmap.Expand(rs.Attributes, "security_groups").([]interface{})
		elbOpts.SecurityGroups = expandStringList(v)
	}

	if _, ok := rs.Attributes["subnets.#"]; ok {
		v = flatmap.Expand(rs.Attributes, "subnets").([]interface{})
		elbOpts.Subnets = expandStringList(v)
	}

	log.Printf("[DEBUG] ELB create configuration: %#v", elbOpts)

	_, err = elbconn.CreateLoadBalancer(elbOpts)
	if err != nil {
		return nil, fmt.Errorf("Error creating ELB: %s", err)
	}

	// Assign the elb's unique identifier for use later
	rs.ID = elbName
	log.Printf("[INFO] ELB ID: %s", elbName)

	if _, ok := rs.Attributes["instances.#"]; ok {
		// If we have any instances, we need to register them
		v = flatmap.Expand(rs.Attributes, "instances").([]interface{})
		instances := expandStringList(v)

		if len(instances) > 0 {
			registerInstancesOpts := elb.RegisterInstancesWithLoadBalancer{
				LoadBalancerName: elbName,
				Instances:        instances,
			}

			_, err := elbconn.RegisterInstancesWithLoadBalancer(&registerInstancesOpts)

			if err != nil {
				return rs, fmt.Errorf("Failure registering instances: %s", err)
			}
		}
	}

	if _, ok := rs.Attributes["health_check.#"]; ok {
		v := flatmap.Expand(rs.Attributes, "health_check").([]interface{})
		health_check := v[0].(map[string]interface{})
		healthyThreshold, err := strconv.ParseInt(health_check["healthy_threshold"].(string), 0, 0)
		unhealthyThreshold, err := strconv.ParseInt(health_check["unhealthy_threshold"].(string), 0, 0)
		interval, err := strconv.ParseInt(health_check["interval"].(string), 0, 0)
		timeout, err := strconv.ParseInt(health_check["timeout"].(string), 0, 0)

		if err != nil {
			return nil, err
		}

		configureHealthCheckOpts := elb.ConfigureHealthCheck{
			LoadBalancerName: elbName,
			Check: elb.HealthCheck{
				HealthyThreshold:   healthyThreshold,
				UnhealthyThreshold: unhealthyThreshold,
				Interval:           interval,
				Target:             health_check["target"].(string),
				Timeout:            timeout,
			},
		}

		_, err = elbconn.ConfigureHealthCheck(&configureHealthCheckOpts)
		if err != nil {
			return rs, fmt.Errorf("Failure configuring health check: %s", err)
		}
	}

	loadBalancer, err := resource_aws_elb_retrieve_balancer(rs.ID, elbconn)
	if err != nil {
		return rs, err
	}

	return resource_aws_elb_update_state(rs, loadBalancer)
}

func resource_aws_elb_update(
	s *terraform.ResourceState,
	d *terraform.ResourceDiff,
	meta interface{}) (*terraform.ResourceState, error) {
	p := meta.(*ResourceProvider)
	elbconn := p.elbconn

	rs := s.MergeDiff(d)

	// If we currently have instances, or did have instances,
	// we want to figure out what to add and remove from the load
	// balancer
	if attr, ok := d.Attributes["instances.#"]; ok && attr.Old != "" {
		// The new state of instances merged with the diff
		mergedInstances := expandStringList(flatmap.Expand(
			rs.Attributes, "instances").([]interface{}))

		// The state before the diff merge
		previousInstances := expandStringList(flatmap.Expand(
			s.Attributes, "instances").([]interface{}))

		// keep track of what instances we are removing, and which
		// we are adding
		var toRemove []string
		var toAdd []string

		for _, instanceId := range mergedInstances {
			for _, prevId := range previousInstances {
				// If the merged instance ID existed
				// previously, we don't have to do anything
				if instanceId == prevId {
					continue
					// Otherwise, we need to add it to the load balancer
				} else {
					toAdd = append(toAdd, instanceId)
				}
			}
		}

		for i, instanceId := range toAdd {
			for _, prevId := range previousInstances {
				// If the instance ID we are adding existed
				// previously, we want to not add it, but rather remove
				// it
				if instanceId == prevId {
					toRemove = append(toRemove, instanceId)
					toAdd = append(toAdd[:i], toAdd[i+1:]...)
					// Otherwise, we continue adding it to the ELB
				} else {
					continue
				}
			}
		}

		if len(toAdd) > 0 {
			registerInstancesOpts := elb.RegisterInstancesWithLoadBalancer{
				LoadBalancerName: rs.ID,
				Instances:        toAdd,
			}

			_, err := elbconn.RegisterInstancesWithLoadBalancer(&registerInstancesOpts)

			if err != nil {
				return s, fmt.Errorf("Failure registering instances: %s", err)
			}
		}

		if len(toRemove) > 0 {
			deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancer{
				LoadBalancerName: rs.ID,
				Instances:        toRemove,
			}

			_, err := elbconn.DeregisterInstancesFromLoadBalancer(&deRegisterInstancesOpts)

			if err != nil {
				return s, fmt.Errorf("Failure deregistering instances: %s", err)
			}
		}
	}

	loadBalancer, err := resource_aws_elb_retrieve_balancer(rs.ID, elbconn)

	if err != nil {
		return s, err
	}

	return resource_aws_elb_update_state(rs, loadBalancer)
}

func resource_aws_elb_destroy(
	s *terraform.ResourceState,
	meta interface{}) error {
	p := meta.(*ResourceProvider)
	elbconn := p.elbconn

	log.Printf("[INFO] Deleting ELB: %s", s.ID)

	// Destroy the load balancer
	deleteElbOpts := elb.DeleteLoadBalancer{
		LoadBalancerName: s.ID,
	}
	_, err := elbconn.DeleteLoadBalancer(&deleteElbOpts)

	if err != nil {
		return fmt.Errorf("Error deleting ELB: %s", err)
	}

	return nil
}

func resource_aws_elb_refresh(
	s *terraform.ResourceState,
	meta interface{}) (*terraform.ResourceState, error) {
	p := meta.(*ResourceProvider)
	elbconn := p.elbconn

	loadBalancer, err := resource_aws_elb_retrieve_balancer(s.ID, elbconn)
	if err != nil {
		return nil, err
	}

	return resource_aws_elb_update_state(s, loadBalancer)
}

func resource_aws_elb_diff(
	s *terraform.ResourceState,
	c *terraform.ResourceConfig,
	meta interface{}) (*terraform.ResourceDiff, error) {

	b := &diff.ResourceBuilder{
		Attrs: map[string]diff.AttrType{
			"name":              diff.AttrTypeCreate,
			"availability_zone": diff.AttrTypeCreate,
			"security_groups":   diff.AttrTypeCreate, // TODO could be AttrTypeUpdate
			"subnets":           diff.AttrTypeCreate, // TODO could be AttrTypeUpdate
			"listener":          diff.AttrTypeCreate,
			"instances":         diff.AttrTypeUpdate,
			"health_check":      diff.AttrTypeCreate,
		},

		ComputedAttrs: []string{
			"dns_name",
		},
	}

	return b.Diff(s, c)
}

func resource_aws_elb_update_state(
	s *terraform.ResourceState,
	balancer *elb.LoadBalancer) (*terraform.ResourceState, error) {

	s.Attributes["name"] = balancer.LoadBalancerName
	s.Attributes["dns_name"] = balancer.DNSName

	// Flatten our group values
	toFlatten := make(map[string]interface{})

	if len(balancer.Instances) > 0 && balancer.Instances[0].InstanceId != "" {
		toFlatten["instances"] = flattenInstances(balancer.Instances)
	}

	if len(balancer.SecurityGroups) > 0 && balancer.SecurityGroups[0] != "" {
		toFlatten["security_groups"] = balancer.SecurityGroups
	}

	if len(balancer.Subnets) > 0 && balancer.Subnets[0] != "" {
		toFlatten["subnets"] = balancer.Subnets
	}

	// There's only one health check, so save that to state as we
	// currently can
	if balancer.HealthCheck.Target != "" {
		toFlatten["health_check"] = flattenHealthCheck(balancer.HealthCheck)
	}

	for k, v := range flatmap.Flatten(toFlatten) {
		s.Attributes[k] = v
	}

	return s, nil
}

// retrieves an ELB by its ID
func resource_aws_elb_retrieve_balancer(id string, elbconn *elb.ELB) (*elb.LoadBalancer, error) {
	describeElbOpts := &elb.DescribeLoadBalancer{
		Names: []string{id},
	}

	// Retrieve the ELB properties for updating the state
	describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts)
	if err != nil {
		return nil, fmt.Errorf("Error retrieving ELB: %s", err)
	}

	loadBalancer := describeResp.LoadBalancers[0]

	// Verify AWS returned our ELB
	if len(describeResp.LoadBalancers) != 1 ||
		describeResp.LoadBalancers[0].LoadBalancerName != id {
		if err != nil {
			return nil, fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancers)
		}
	}

	return &loadBalancer, nil
}

func resource_aws_elb_validation() *config.Validator {
	return &config.Validator{
		Required: []string{
			"name",
			"listener.*",
			"listener.*.instance_port",
			"listener.*.instance_protocol",
			"listener.*.lb_port",
			"listener.*.lb_protocol",
		},
		Optional: []string{
			"instances.*",
			"availability_zones.*",
			"security_groups.*",
			"subnets.*",
			"health_check.#",
			"health_check.0.healthy_threshold",
			"health_check.0.unhealthy_threshold",
			"health_check.0.interval",
			"health_check.0.target",
			"health_check.0.timeout",
		},
	}
}
<commit_msg>added missing internal ELB option<commit_after>package aws

import (
	"fmt"
	"log"
	"strconv"

	"github.com/hashicorp/terraform/flatmap"
	"github.com/hashicorp/terraform/helper/config"
	"github.com/hashicorp/terraform/helper/diff"
	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/goamz/elb"
)

func resource_aws_elb_create(
	s *terraform.ResourceState,
	d *terraform.ResourceDiff,
	meta interface{}) (*terraform.ResourceState, error) {
	p := meta.(*ResourceProvider)
	elbconn := p.elbconn

	// Merge the diff into the state so that we have all the attributes
	// properly.
	rs := s.MergeDiff(d)

	// The name specified for the ELB. This is also our unique ID
	// we save to state if the creation is successful (amazon verifies
	// it is unique)
	elbName := rs.Attributes["name"]

	// Expand the "listener" array to goamz compat []elb.Listener
	v := flatmap.Expand(rs.Attributes, "listener").([]interface{})
	listeners, err := expandListeners(v)
	if err != nil {
		return nil, err
	}

	// Provision the elb
	elbOpts := &elb.CreateLoadBalancer{
		LoadBalancerName: elbName,
		Listeners:        listeners,
	}

	if rs.Attributes["internal"] == "true" {
		elbOpts.Internal = true
	}

	if _, ok := rs.Attributes["availability_zones.#"]; ok {
		v = flatmap.Expand(rs.Attributes, "availability_zones").([]interface{})
		elbOpts.AvailZone = expandStringList(v)
	}

	if _, ok := rs.Attributes["security_groups.#"]; ok {
		v = flatmap.Expand(rs.Attributes, "security_groups").([]interface{})
		elbOpts.SecurityGroups = expandStringList(v)
	}

	if _, ok := rs.Attributes["subnets.#"]; ok {
		v = flatmap.Expand(rs.Attributes, "subnets").([]interface{})
		elbOpts.Subnets = expandStringList(v)
	}

	log.Printf("[DEBUG] ELB create configuration: %#v", elbOpts)

	_, err = elbconn.CreateLoadBalancer(elbOpts)
	if err != nil {
		return nil, fmt.Errorf("Error creating ELB: %s", err)
	}

	// Assign the elb's unique identifier for use later
	rs.ID = elbName
	log.Printf("[INFO] ELB ID: %s", elbName)

	if _, ok := rs.Attributes["instances.#"]; ok {
		// If we have any instances, we need to register them
		v = flatmap.Expand(rs.Attributes, "instances").([]interface{})
		instances := expandStringList(v)

		if len(instances) > 0 {
			registerInstancesOpts := elb.RegisterInstancesWithLoadBalancer{
				LoadBalancerName: elbName,
				Instances:        instances,
			}

			_, err := elbconn.RegisterInstancesWithLoadBalancer(&registerInstancesOpts)

			if err != nil {
				return rs, fmt.Errorf("Failure registering instances: %s", err)
			}
		}
	}

	if _, ok := rs.Attributes["health_check.#"]; ok {
		v := flatmap.Expand(rs.Attributes, "health_check").([]interface{})
		health_check := v[0].(map[string]interface{})
		healthyThreshold, err := strconv.ParseInt(health_check["healthy_threshold"].(string), 0, 0)
		unhealthyThreshold, err := strconv.ParseInt(health_check["unhealthy_threshold"].(string), 0, 0)
		interval, err := strconv.ParseInt(health_check["interval"].(string), 0, 0)
		timeout, err := strconv.ParseInt(health_check["timeout"].(string), 0, 0)

		if err != nil {
			return nil, err
		}

		configureHealthCheckOpts := elb.ConfigureHealthCheck{
			LoadBalancerName: elbName,
			Check: elb.HealthCheck{
				HealthyThreshold:   healthyThreshold,
				UnhealthyThreshold: unhealthyThreshold,
				Interval:           interval,
				Target:             health_check["target"].(string),
				Timeout:            timeout,
			},
		}

		_, err = elbconn.ConfigureHealthCheck(&configureHealthCheckOpts)
		if err != nil {
			return rs, fmt.Errorf("Failure configuring health check: %s", err)
		}
	}

	loadBalancer, err := resource_aws_elb_retrieve_balancer(rs.ID, elbconn)
	if err != nil {
		return rs, err
	}

	return resource_aws_elb_update_state(rs, loadBalancer)
}

func resource_aws_elb_update(
	s *terraform.ResourceState,
	d *terraform.ResourceDiff,
	meta interface{}) (*terraform.ResourceState, error) {
	p := meta.(*ResourceProvider)
	elbconn := p.elbconn

	rs := s.MergeDiff(d)

	// If we currently have instances, or did have instances,
	// we want to figure out what to add and remove from the load
	// balancer
	if attr, ok := d.Attributes["instances.#"]; ok && attr.Old != "" {
		// The new state of instances merged with the diff
		mergedInstances := expandStringList(flatmap.Expand(
			rs.Attributes, "instances").([]interface{}))

		// The state before the diff merge
		previousInstances := expandStringList(flatmap.Expand(
			s.Attributes, "instances").([]interface{}))

		// keep track of what instances we are removing, and which
		// we are adding
		var toRemove []string
		var toAdd []string

		for _, instanceId := range mergedInstances {
			for _, prevId := range previousInstances {
				// If the merged instance ID existed
				// previously, we don't have to do anything
				if instanceId == prevId {
					continue
					// Otherwise, we need to add it to the load balancer
				} else {
					toAdd = append(toAdd, instanceId)
				}
			}
		}

		for i, instanceId := range toAdd {
			for _, prevId := range previousInstances {
				// If the instance ID we are adding existed
				// previously, we want to not add it, but rather remove
				// it
				if instanceId == prevId {
					toRemove = append(toRemove, instanceId)
					toAdd = append(toAdd[:i], toAdd[i+1:]...)
					// Otherwise, we continue adding it to the ELB
				} else {
					continue
				}
			}
		}

		if len(toAdd) > 0 {
			registerInstancesOpts := elb.RegisterInstancesWithLoadBalancer{
				LoadBalancerName: rs.ID,
				Instances:        toAdd,
			}

			_, err := elbconn.RegisterInstancesWithLoadBalancer(&registerInstancesOpts)

			if err != nil {
				return s, fmt.Errorf("Failure registering instances: %s", err)
			}
		}

		if len(toRemove) > 0 {
			deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancer{
				LoadBalancerName: rs.ID,
				Instances:        toRemove,
			}

			_, err := elbconn.DeregisterInstancesFromLoadBalancer(&deRegisterInstancesOpts)

			if err != nil {
				return s, fmt.Errorf("Failure deregistering instances: %s", err)
			}
		}
	}

	loadBalancer, err := resource_aws_elb_retrieve_balancer(rs.ID, elbconn)

	if err != nil {
		return s, err
	}

	return resource_aws_elb_update_state(rs, loadBalancer)
}

func resource_aws_elb_destroy(
	s *terraform.ResourceState,
	meta interface{}) error {
	p := meta.(*ResourceProvider)
	elbconn := p.elbconn

	log.Printf("[INFO] Deleting ELB: %s", s.ID)

	// Destroy the load balancer
	deleteElbOpts := elb.DeleteLoadBalancer{
		LoadBalancerName: s.ID,
	}
	_, err := elbconn.DeleteLoadBalancer(&deleteElbOpts)

	if err != nil {
		return fmt.Errorf("Error deleting ELB: %s", err)
	}

	return nil
}

func resource_aws_elb_refresh(
	s *terraform.ResourceState,
	meta interface{}) (*terraform.ResourceState, error) {
	p := meta.(*ResourceProvider)
	elbconn := p.elbconn

	loadBalancer, err := resource_aws_elb_retrieve_balancer(s.ID, elbconn)
	if err != nil {
		return nil, err
	}

	return resource_aws_elb_update_state(s, loadBalancer)
}

func resource_aws_elb_diff(
	s *terraform.ResourceState,
	c *terraform.ResourceConfig,
	meta interface{}) (*terraform.ResourceDiff, error) {

	b := &diff.ResourceBuilder{
		Attrs: map[string]diff.AttrType{
			"name":              diff.AttrTypeCreate,
			"availability_zone": diff.AttrTypeCreate,
			"security_groups":   diff.AttrTypeCreate, // TODO could be AttrTypeUpdate
			"subnets":           diff.AttrTypeCreate, // TODO could be AttrTypeUpdate
			"listener":          diff.AttrTypeCreate,
			"instances":         diff.AttrTypeUpdate,
			"health_check":      diff.AttrTypeCreate,
			"internal":          diff.AttrTypeCreate,
		},

		ComputedAttrs: []string{
			"dns_name",
		},
	}

	return b.Diff(s, c)
}

func resource_aws_elb_update_state(
	s *terraform.ResourceState,
	balancer *elb.LoadBalancer) (*terraform.ResourceState, error) {

	s.Attributes["name"] = balancer.LoadBalancerName
	s.Attributes["dns_name"] = balancer.DNSName

	// Flatten our group values
	toFlatten := make(map[string]interface{})

	if len(balancer.Instances) > 0 && balancer.Instances[0].InstanceId != "" {
		toFlatten["instances"] = flattenInstances(balancer.Instances)
	}

	if len(balancer.SecurityGroups) > 0 && balancer.SecurityGroups[0] != "" {
		toFlatten["security_groups"] = balancer.SecurityGroups
	}

	if len(balancer.Subnets) > 0 && balancer.Subnets[0] != "" {
		toFlatten["subnets"] = balancer.Subnets
	}

	// There's only one health check, so save that to state as we
	// currently can
	if balancer.HealthCheck.Target != "" {
		toFlatten["health_check"] = flattenHealthCheck(balancer.HealthCheck)
	}

	for k, v := range flatmap.Flatten(toFlatten) {
		s.Attributes[k] = v
	}

	return s, nil
}

// retrieves an ELB by its ID
func resource_aws_elb_retrieve_balancer(id string, elbconn *elb.ELB) (*elb.LoadBalancer, error) {
	describeElbOpts := &elb.DescribeLoadBalancer{
		Names: []string{id},
	}

	// Retrieve the ELB properties for updating the state
	describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts)
	if err != nil {
		return nil, fmt.Errorf("Error retrieving ELB: %s", err)
	}

	loadBalancer := describeResp.LoadBalancers[0]

	// Verify AWS returned our ELB
	if len(describeResp.LoadBalancers) != 1 ||
		describeResp.LoadBalancers[0].LoadBalancerName != id {
		if err != nil {
			return nil, fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancers)
		}
	}

	return &loadBalancer, nil
}

func resource_aws_elb_validation() *config.Validator {
	return &config.Validator{
		Required: []string{
			"name",
			"listener.*",
			"listener.*.instance_port",
			"listener.*.instance_protocol",
			"listener.*.lb_port",
			"listener.*.lb_protocol",
		},
		Optional: []string{
			"instances.*",
			"internal",
			"availability_zones.*",
			"security_groups.*",
			"subnets.*",
			"health_check.#",
			"health_check.0.healthy_threshold",
			"health_check.0.unhealthy_threshold",
			"health_check.0.interval",
			"health_check.0.target",
			"health_check.0.timeout",
		},
	}
}
<|endoftext|>