<commit_before>package types

// constants.go contains the Sia constants. Depending on which build tags are
// used, the constants will be initialized to different values.
//
// CONTRIBUTE: We don't have way to check that the non-test constants are all
// sane, plus we have no coverage for them.

import (
	"math/big"

	"github.com/rivine/rivine/build"
)

var (
	BlockSizeLimit   = uint64(2e6)
	RootDepth        = Target{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}
	BlockFrequency   BlockHeight
	MaturityDelay    BlockHeight
	GenesisTimestamp Timestamp
	RootTarget       Target

	MedianTimestampWindow  = uint64(11)
	TargetWindow           BlockHeight
	MaxAdjustmentUp        *big.Rat
	MaxAdjustmentDown      *big.Rat
	FutureThreshold        Timestamp
	ExtremeFutureThreshold Timestamp

	StakeModifierDelay BlockHeight

	BlockStakeAging uint64

	BlockCreatorFee Currency

	OneCoin = NewCurrency(new(big.Int).Exp(big.NewInt(10), big.NewInt(24), nil))

	GenesisBlockStakeAllocation = []BlockStakeOutput{}
	GenesisBlockStakeCount      Currency
	GenesisCoinDistribution     = []CoinOutput{}
	GenesisCoinCount            Currency

	GenesisBlock Block

	// GenesisID is used in many places. Calculating it once saves lots of
	// redundant computation.
	GenesisID BlockID

	// StartDifficulty is used in many places. Calculate it once.
	StartDifficulty Difficulty
)

// init checks which build constant is in place and initializes the variables
// accordingly.
func init() {

	if build.Release == "dev" {
		// 'dev' settings are for small developer testnets, usually on the same
		// computer. Settings are slow enough that a small team of developers
		// can coordinate their actions over a the developer testnets, but fast
		// enough that there isn't much time wasted on waiting for things to
		// happen.
		BlockFrequency = 12 // 12 seconds: slow enough for developers to see ~each block, fast enough that blocks don't waste time.
		MaturityDelay = 10  // 120 seconds before a delayed output matures.

		// Change as necessary. If not changed, the first few difficulty addaptions
		// will be wrong, but after some new difficulty calculations the error will
		// fade out.
		GenesisTimestamp = Timestamp(1424139000)

		TargetWindow = 20                        // Difficulty is adjusted based on prior 20 blocks.
		MaxAdjustmentUp = big.NewRat(120, 100)   // Difficulty adjusts quickly.
		MaxAdjustmentDown = big.NewRat(100, 120) // Difficulty adjusts quickly.
		FutureThreshold = 2 * 60                 // 2 minutes.
		ExtremeFutureThreshold = 4 * 60          // 4 minutes.
		StakeModifierDelay = 2000                // Number of blocks to take in history to calculate the stakemodifier

		BlockStakeAging = uint64(1 << 10) // Block stake aging if unspent block stake is not at index 0

		BlockCreatorFee = OneCoin.Mul64(100)

		bso := BlockStakeOutput{
			Value:      NewCurrency64(1000000),
			UnlockHash: UnlockHash{},
		}

		co := CoinOutput{
			Value: OneCoin.Mul64(1000),
		}

		// Seed for this address:
		// across knife thirsty puck itches hazard enmity fainted pebbles unzip echo queen rarest aphid bugs yanks okay abbey eskimos dove orange nouns august ailments inline rebel glass tyrant acumen
		bso.UnlockHash.LoadString("e66bbe9638ae0e998641dc9faa0180c15a1071b1767784cdda11ad3c1d309fa692667931be66")
		GenesisBlockStakeAllocation = append(GenesisBlockStakeAllocation, bso)
		co.UnlockHash.LoadString("e66bbe9638ae0e998641dc9faa0180c15a1071b1767784cdda11ad3c1d309fa692667931be66")
		GenesisCoinDistribution = append(GenesisCoinDistribution, co)

	} else if build.Release == "testing" {
		// 'testing' settings are for automatic testing, and create much faster
		// environments than a human can interact with.
		BlockFrequency = 1 // As fast as possible
		MaturityDelay = 3
		GenesisTimestamp = CurrentTimestamp() - 1e6
		RootTarget = Target{128} // Takes an expected 2 hashes; very fast for testing but still probes 'bad hash' code.

		// A restrictive difficulty clamp prevents the difficulty from climbing
		// during testing, as the resolution on the difficulty adjustment is
		// only 1 second and testing mining should be happening substantially
		// faster than that.
		TargetWindow = 200
		MaxAdjustmentUp = big.NewRat(10001, 10000)
		MaxAdjustmentDown = big.NewRat(9999, 10000)
		FutureThreshold = 3        // 3 seconds
		ExtremeFutureThreshold = 6 // 6 seconds
		StakeModifierDelay = 20

		BlockStakeAging = uint64(1 << 10)

		BlockCreatorFee = OneCoin.Mul64(100)

		GenesisBlockStakeAllocation = []BlockStakeOutput{
			{
				Value:      NewCurrency64(2000),
				UnlockHash: UnlockHash{214, 166, 197, 164, 29, 201, 53, 236, 106, 239, 10, 158, 127, 131, 20, 138, 63, 221, 230, 16, 98, 247, 32, 77, 210, 68, 116, 12, 241, 89, 27, 223},
			},
			{
				Value:      NewCurrency64(7000),
				UnlockHash: UnlockHash{209, 246, 228, 60, 248, 78, 242, 110, 9, 8, 227, 248, 225, 216, 163, 52, 142, 93, 47, 176, 103, 41, 137, 80, 212, 8, 132, 58, 241, 189, 2, 17},
			},
			{
				Value:      NewCurrency64(1000),
				UnlockHash: UnlockConditions{}.UnlockHash(),
			},
		}
	} else if build.Release == "standard" {
		// 'standard' settings are for the full network. They are slow enough
		// that the network is secure in a real-world byzantine environment.

		// A block time of 1 block per 10 minutes is chosen to follow Bitcoin's
		// example. The security lost by lowering the block time is not
		// insignificant, and the convenience gained by lowering the blocktime
		// even down to 90 seconds is not significant. I do feel that 10
		// minutes could even be too short, but it has worked well for Bitcoin.
		BlockFrequency = 600

		// Payouts take 1 day to mature. This is to prevent a class of double
		// spending attacks parties unintentionally spend coins that will stop
		// existing after a blockchain reorganization. There are multiple
		// classes of payouts in Sia that depend on a previous block - if that
		// block changes, then the output changes and the previously existing
		// output ceases to exist. This delay stops both unintentional double
		// spending and stops a small set of long-range mining attacks.
		MaturityDelay = 144

		// The genesis timestamp is set to June 1st, 2017
		GenesisTimestamp = Timestamp(1496322000) // June 2nd, 2017 @ 1:00pm UTC.

		// The RootTarget was set such that the developers could reasonable
		// premine 100 blocks in a day. It was known to the developrs at launch
		// this this was at least one and perhaps two orders of magnitude too
		// small.
		RootTarget = Target{0, 0, 0, 0, 32}

		// When the difficulty is adjusted, it is adjusted by looking at the
		// timestamp of the 1000th previous block. This minimizes the abilities
		// of miners to attack the network using rogue timestamps.
		TargetWindow = 1e3

		// The difficutly adjustment is clamped to 2.5x every 500 blocks. This
		// corresponds to 6.25x every 2 weeks, which can be compared to
		// Bitcoin's clamp of 4x every 2 weeks. The difficulty clamp is
		// primarily to stop difficulty raising attacks. Sia's safety margin is
		// similar to Bitcoin's despite the looser clamp because Sia's
		// difficulty is adjusted four times as often. This does result in
		// greater difficulty oscillation, a tradeoff that was chosen to be
		// acceptable due to Sia's more vulnerable position as an altcoin.
		MaxAdjustmentUp = big.NewRat(25, 10)
		MaxAdjustmentDown = big.NewRat(10, 25)

		// Blocks will not be accepted if their timestamp is more than 3 hours
		// into the future, but will be accepted as soon as they are no longer
		// 3 hours into the future. Blocks that are greater than 5 hours into
		// the future are rejected outright, as it is assumed that by the time
		// 2 hours have passed, those blocks will no longer be on the longest
		// chain. Blocks cannot be kept forever because this opens a DoS
		// vector.
		FutureThreshold = 3 * 60 * 60        // 3 hours.
		ExtremeFutureThreshold = 5 * 60 * 60 // 5 hours.

		// The stakemodifier is calculated from blocks in history. The stakemodifier
		// is calculated as: For x = 0 to 255
		// bit x of Stake Modifier = bit x of h(block N-(StakeModifierDelay+x))
		StakeModifierDelay = 2000

		// Blockstakeaging is the number of seconds to wait before blockstake can be
		// used to solve blocks. But only when the block stake output is not the
		// first transaction with the first index. (2^16s < 1 day < 2^17s)
		BlockStakeAging = uint64(1 << 17)

		// BlockCreatorFee is the asset you get when creating a block on top of the
		// other fee.
		BlockCreatorFee = OneCoin.Mul64(10)

		bso := BlockStakeOutput{
			Value:      NewCurrency64(1000000),
			UnlockHash: UnlockHash{},
		}

		co := CoinOutput{
			Value: OneCoin.Mul64(100 * 1000 * 1000),
		}

		bso.UnlockHash.LoadString("b5e42056ef394f2ad9b511a61cec874d25bebe2095682dd37455cbafed4bec15c28ee7d7ed1d")
		GenesisBlockStakeAllocation = append(GenesisBlockStakeAllocation, bso)
		co.UnlockHash.LoadString("b5e42056ef394f2ad9b511a61cec874d25bebe2095682dd37455cbafed4bec15c28ee7d7ed1d")
		GenesisCoinDistribution = append(GenesisCoinDistribution, co)
	}

	CalculateGenesis()
}

// CalculateGenesis fills in the genesis block variables which are computed based on
// variables that have been set earlier
func CalculateGenesis() {
	// Create the genesis block.
	GenesisBlock = Block{
		Timestamp: GenesisTimestamp,
		Transactions: []Transaction{
			{
				BlockStakeOutputs: GenesisBlockStakeAllocation,
				CoinOutputs:       GenesisCoinDistribution,
			},
		},
	}
	// Calculate the genesis ID.
	GenesisID = GenesisBlock.ID()

	for _, bso := range GenesisBlockStakeAllocation {
		GenesisBlockStakeCount = GenesisBlockStakeCount.Add(bso.Value)
	}
	for _, co := range GenesisCoinDistribution {
		GenesisCoinCount = GenesisCoinCount.Add(co.Value)
	}

	//Calculate start difficulty
	StartDifficulty = NewDifficulty(big.NewInt(0).Mul(big.NewInt(int64(BlockFrequency)), GenesisBlockStakeCount.Big()))
	RootTarget = NewTarget(StartDifficulty)

}
<commit_msg>update dev wallet seed<commit_after>package types

// constants.go contains the Sia constants. Depending on which build tags are
// used, the constants will be initialized to different values.
//
// CONTRIBUTE: We don't have way to check that the non-test constants are all
// sane, plus we have no coverage for them.

import (
	"math/big"

	"github.com/rivine/rivine/build"
)

var (
	BlockSizeLimit   = uint64(2e6)
	RootDepth        = Target{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}
	BlockFrequency   BlockHeight
	MaturityDelay    BlockHeight
	GenesisTimestamp Timestamp
	RootTarget       Target

	MedianTimestampWindow  = uint64(11)
	TargetWindow           BlockHeight
	MaxAdjustmentUp        *big.Rat
	MaxAdjustmentDown      *big.Rat
	FutureThreshold        Timestamp
	ExtremeFutureThreshold Timestamp

	StakeModifierDelay BlockHeight

	BlockStakeAging uint64

	BlockCreatorFee Currency

	OneCoin = NewCurrency(new(big.Int).Exp(big.NewInt(10), big.NewInt(24), nil))

	GenesisBlockStakeAllocation = []BlockStakeOutput{}
	GenesisBlockStakeCount      Currency
	GenesisCoinDistribution     = []CoinOutput{}
	GenesisCoinCount            Currency

	GenesisBlock Block

	// GenesisID is used in many places. Calculating it once saves lots of
	// redundant computation.
	GenesisID BlockID

	// StartDifficulty is used in many places. Calculate it once.
	StartDifficulty Difficulty
)

// init checks which build constant is in place and initializes the variables
// accordingly.
func init() {

	if build.Release == "dev" {
		// 'dev' settings are for small developer testnets, usually on the same
		// computer. Settings are slow enough that a small team of developers
		// can coordinate their actions over a the developer testnets, but fast
		// enough that there isn't much time wasted on waiting for things to
		// happen.
		BlockFrequency = 12 // 12 seconds: slow enough for developers to see ~each block, fast enough that blocks don't waste time.
		MaturityDelay = 10  // 120 seconds before a delayed output matures.

		// Change as necessary. If not changed, the first few difficulty addaptions
		// will be wrong, but after some new difficulty calculations the error will
		// fade out.
		GenesisTimestamp = Timestamp(1424139000)

		TargetWindow = 20                        // Difficulty is adjusted based on prior 20 blocks.
		MaxAdjustmentUp = big.NewRat(120, 100)   // Difficulty adjusts quickly.
		MaxAdjustmentDown = big.NewRat(100, 120) // Difficulty adjusts quickly.
		FutureThreshold = 2 * 60                 // 2 minutes.
		ExtremeFutureThreshold = 4 * 60          // 4 minutes.
		StakeModifierDelay = 2000                // Number of blocks to take in history to calculate the stakemodifier

		BlockStakeAging = uint64(1 << 10) // Block stake aging if unspent block stake is not at index 0

		BlockCreatorFee = OneCoin.Mul64(100)

		bso := BlockStakeOutput{
			Value:      NewCurrency64(1000000),
			UnlockHash: UnlockHash{},
		}

		co := CoinOutput{
			Value: OneCoin.Mul64(1000),
		}

		// Seed for this address:
		// recall view document apology stone tattoo job farm pilot favorite mango topic thing dilemma dawn width marble proud pen meadow sing museum lucky present
		bso.UnlockHash.LoadString("e66bbe9638ae0e998641dc9faa0180c15a1071b1767784cdda11ad3c1d309fa692667931be66")
		GenesisBlockStakeAllocation = append(GenesisBlockStakeAllocation, bso)
		co.UnlockHash.LoadString("e66bbe9638ae0e998641dc9faa0180c15a1071b1767784cdda11ad3c1d309fa692667931be66")
		GenesisCoinDistribution = append(GenesisCoinDistribution, co)

	} else if build.Release == "testing" {
		// 'testing' settings are for automatic testing, and create much faster
		// environments than a human can interact with.
		BlockFrequency = 1 // As fast as possible
		MaturityDelay = 3
		GenesisTimestamp = CurrentTimestamp() - 1e6
		RootTarget = Target{128} // Takes an expected 2 hashes; very fast for testing but still probes 'bad hash' code.

		// A restrictive difficulty clamp prevents the difficulty from climbing
		// during testing, as the resolution on the difficulty adjustment is
		// only 1 second and testing mining should be happening substantially
		// faster than that.
		TargetWindow = 200
		MaxAdjustmentUp = big.NewRat(10001, 10000)
		MaxAdjustmentDown = big.NewRat(9999, 10000)
		FutureThreshold = 3        // 3 seconds
		ExtremeFutureThreshold = 6 // 6 seconds
		StakeModifierDelay = 20

		BlockStakeAging = uint64(1 << 10)

		BlockCreatorFee = OneCoin.Mul64(100)

		GenesisBlockStakeAllocation = []BlockStakeOutput{
			{
				Value:      NewCurrency64(2000),
				UnlockHash: UnlockHash{214, 166, 197, 164, 29, 201, 53, 236, 106, 239, 10, 158, 127, 131, 20, 138, 63, 221, 230, 16, 98, 247, 32, 77, 210, 68, 116, 12, 241, 89, 27, 223},
			},
			{
				Value:      NewCurrency64(7000),
				UnlockHash: UnlockHash{209, 246, 228, 60, 248, 78, 242, 110, 9, 8, 227, 248, 225, 216, 163, 52, 142, 93, 47, 176, 103, 41, 137, 80, 212, 8, 132, 58, 241, 189, 2, 17},
			},
			{
				Value:      NewCurrency64(1000),
				UnlockHash: UnlockConditions{}.UnlockHash(),
			},
		}
	} else if build.Release == "standard" {
		// 'standard' settings are for the full network. They are slow enough
		// that the network is secure in a real-world byzantine environment.

		// A block time of 1 block per 10 minutes is chosen to follow Bitcoin's
		// example. The security lost by lowering the block time is not
		// insignificant, and the convenience gained by lowering the blocktime
		// even down to 90 seconds is not significant. I do feel that 10
		// minutes could even be too short, but it has worked well for Bitcoin.
		BlockFrequency = 600

		// Payouts take 1 day to mature. This is to prevent a class of double
		// spending attacks parties unintentionally spend coins that will stop
		// existing after a blockchain reorganization. There are multiple
		// classes of payouts in Sia that depend on a previous block - if that
		// block changes, then the output changes and the previously existing
		// output ceases to exist. This delay stops both unintentional double
		// spending and stops a small set of long-range mining attacks.
		MaturityDelay = 144

		// The genesis timestamp is set to June 1st, 2017
		GenesisTimestamp = Timestamp(1496322000) // June 2nd, 2017 @ 1:00pm UTC.

		// The RootTarget was set such that the developers could reasonable
		// premine 100 blocks in a day. It was known to the developrs at launch
		// this this was at least one and perhaps two orders of magnitude too
		// small.
		RootTarget = Target{0, 0, 0, 0, 32}

		// When the difficulty is adjusted, it is adjusted by looking at the
		// timestamp of the 1000th previous block. This minimizes the abilities
		// of miners to attack the network using rogue timestamps.
		TargetWindow = 1e3

		// The difficutly adjustment is clamped to 2.5x every 500 blocks. This
		// corresponds to 6.25x every 2 weeks, which can be compared to
		// Bitcoin's clamp of 4x every 2 weeks. The difficulty clamp is
		// primarily to stop difficulty raising attacks. Sia's safety margin is
		// similar to Bitcoin's despite the looser clamp because Sia's
		// difficulty is adjusted four times as often. This does result in
		// greater difficulty oscillation, a tradeoff that was chosen to be
		// acceptable due to Sia's more vulnerable position as an altcoin.
		MaxAdjustmentUp = big.NewRat(25, 10)
		MaxAdjustmentDown = big.NewRat(10, 25)

		// Blocks will not be accepted if their timestamp is more than 3 hours
		// into the future, but will be accepted as soon as they are no longer
		// 3 hours into the future. Blocks that are greater than 5 hours into
		// the future are rejected outright, as it is assumed that by the time
		// 2 hours have passed, those blocks will no longer be on the longest
		// chain. Blocks cannot be kept forever because this opens a DoS
		// vector.
		FutureThreshold = 3 * 60 * 60        // 3 hours.
		ExtremeFutureThreshold = 5 * 60 * 60 // 5 hours.

		// The stakemodifier is calculated from blocks in history. The stakemodifier
		// is calculated as: For x = 0 to 255
		// bit x of Stake Modifier = bit x of h(block N-(StakeModifierDelay+x))
		StakeModifierDelay = 2000

		// Blockstakeaging is the number of seconds to wait before blockstake can be
		// used to solve blocks. But only when the block stake output is not the
		// first transaction with the first index. (2^16s < 1 day < 2^17s)
		BlockStakeAging = uint64(1 << 17)

		// BlockCreatorFee is the asset you get when creating a block on top of the
		// other fee.
		BlockCreatorFee = OneCoin.Mul64(10)

		bso := BlockStakeOutput{
			Value:      NewCurrency64(1000000),
			UnlockHash: UnlockHash{},
		}

		co := CoinOutput{
			Value: OneCoin.Mul64(100 * 1000 * 1000),
		}

		bso.UnlockHash.LoadString("b5e42056ef394f2ad9b511a61cec874d25bebe2095682dd37455cbafed4bec15c28ee7d7ed1d")
		GenesisBlockStakeAllocation = append(GenesisBlockStakeAllocation, bso)
		co.UnlockHash.LoadString("b5e42056ef394f2ad9b511a61cec874d25bebe2095682dd37455cbafed4bec15c28ee7d7ed1d")
		GenesisCoinDistribution = append(GenesisCoinDistribution, co)
	}

	CalculateGenesis()
}

// CalculateGenesis fills in the genesis block variables which are computed based on
// variables that have been set earlier
func CalculateGenesis() {
	// Create the genesis block.
	GenesisBlock = Block{
		Timestamp: GenesisTimestamp,
		Transactions: []Transaction{
			{
				BlockStakeOutputs: GenesisBlockStakeAllocation,
				CoinOutputs:       GenesisCoinDistribution,
			},
		},
	}
	// Calculate the genesis ID.
	GenesisID = GenesisBlock.ID()

	for _, bso := range GenesisBlockStakeAllocation {
		GenesisBlockStakeCount = GenesisBlockStakeCount.Add(bso.Value)
	}
	for _, co := range GenesisCoinDistribution {
		GenesisCoinCount = GenesisCoinCount.Add(co.Value)
	}

	//Calculate start difficulty
	StartDifficulty = NewDifficulty(big.NewInt(0).Mul(big.NewInt(int64(BlockFrequency)), GenesisBlockStakeCount.Big()))
	RootTarget = NewTarget(StartDifficulty)

}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mapper

import (
	"errors"

	"github.com/ernestio/definition-mapper/libmapper"
	"github.com/ernestio/definition-mapper/libmapper/providers/aws/components"
	def "github.com/ernestio/definition-mapper/libmapper/providers/aws/definition"
	"github.com/mitchellh/mapstructure"
	graph "gopkg.in/r3labs/graph.v2"
)

// SUPPORTEDCOMPONENTS represents all component types supported by ernest
var SUPPORTEDCOMPONENTS = []string{"vpc", "internet_gateway", "network", "instance", "firewall", "nat_gateway", "elb", "ebs", "s3", "route53", "rds_instance", "rds_cluster"}

// Mapper : implements the generic mapper structure
type Mapper struct{}

// New : returns a new aws mapper
func New() libmapper.Mapper {
	return &Mapper{}
}

// ConvertDefinition : converts the input yaml definition to a graph format
func (m Mapper) ConvertDefinition(gd libmapper.Definition) (*graph.Graph, error) {
	g := graph.New()

	d, ok := gd.(*def.Definition)
	if ok != true {
		return g, errors.New("Could not convert generic definition into aws format")
	}

	// Map basic component values from definition
	err := mapComponents(d, g)
	if err != nil {
		return g, err
	}

	for _, c := range g.Components {
		c.Rebuild(g)

		// Validate Components
		err := c.Validate()
		if err != nil {
			return g, err
		}

		// Build internal & template values
		for _, dep := range c.Dependencies() {
			if g.HasComponent(dep) != true {
				return g, errors.New("Could not resolve component dependency: " + dep)
			}
		}

		// Build dependencies
		for _, dep := range c.Dependencies() {
			g.Connect(dep, c.GetID())
		}
	}

	return g, nil
}

// ConvertGraph : converts the service graph into an input yaml format
func (m Mapper) ConvertGraph(g *graph.Graph) (libmapper.Definition, error) {
	var d def.Definition

	for _, c := range g.Components {
		c.Rebuild(g)

		for _, dep := range c.Dependencies() {
			if g.HasComponent(dep) != true {
				return g, errors.New("Could not resolve component dependency: " + dep)
			}
		}

		err := c.Validate()
		if err != nil {
			return d, err
		}
	}

	d.Vpcs = MapDefinitionVpcs(g)
	d.Networks = MapDefinitionNetworks(g)
	d.Instances = MapDefinitionInstances(g)
	d.SecurityGroups = MapDefinitionSecurityGroups(g)
	d.ELBs = MapDefinitionELBs(g)
	d.EBSVolumes = MapDefinitionEBSVolumes(g)
	d.NatGateways = MapDefinitionNats(g)
	d.RDSClusters = MapDefinitionRDSClusters(g)
	d.RDSInstances = MapDefinitionRDSInstances(g)
	d.Route53Zones = MapDefinitionRoute53Zones(g)
	d.S3Buckets = MapDefinitionS3Buckets(g)

	return d, nil
}

// LoadDefinition : returns an aws type definition
func (m Mapper) LoadDefinition(gd map[string]interface{}) (libmapper.Definition, error) {
	var d def.Definition

	err := d.LoadMap(gd)

	return &d, err
}

// LoadGraph : returns a generic interal graph
func (m Mapper) LoadGraph(gg map[string]interface{}) (*graph.Graph, error) {
	g := graph.New()

	g.Load(gg)

	for i := 0; i < len(g.Components); i++ {
		gc := g.Components[i].(*graph.GenericComponent)

		var c graph.Component

		switch gc.GetType() {
		case "vpc":
			c = &components.Vpc{}
		case "network":
			c = &components.Network{}
		case "internet_gateway":
			c = &components.InternetGateway{}
		case "instance":
			c = &components.Instance{}
		case "firewall":
			c = &components.SecurityGroup{}
		case "elb":
			c = &components.ELB{}
		case "ebs_volume":
			c = &components.EBSVolume{}
		case "nat":
			c = &components.NatGateway{}
		case "rds_cluster":
			c = &components.RDSCluster{}
		case "rds_instance":
			c = &components.RDSInstance{}
		case "route53":
			c = &components.Route53Zone{}
		case "s3":
			c = &components.S3Bucket{}
		default:
			continue
		}

		config := &mapstructure.DecoderConfig{
			Metadata: nil,
			Result:   c,
			TagName:  "json",
		}

		decoder, err := mapstructure.NewDecoder(config)
		if err != nil {
			return g, err
		}

		err = decoder.Decode(gc)
		if err != nil {
			return g, err
		}

		g.Components[i] = c
	}

	return g, nil
}

// CreateImportGraph : creates a new graph with component queries used to import components from a provider
func (m Mapper) CreateImportGraph(params []string) *graph.Graph {
	g := graph.New()
	filter := make(map[string]string)

	if len(params) > 0 {
		filter["ernest.service"] = params[0]
	}

	for _, ctype := range SUPPORTEDCOMPONENTS {
		q := MapQuery(ctype+"s", filter)
		g.AddComponent(q)
	}

	return g
}

// ProviderCredentials : maps aws credentials to a generic component
func (m Mapper) ProviderCredentials(details map[string]interface{}) graph.Component {
	credentials := make(graph.GenericComponent)

	credentials["_action"] = "none"
	credentials["_component"] = "credentials"
	credentials["_component_id"] = "credentials::aws"
	credentials["_provider"] = details["type"]
	credentials["name"] = details["name"]
	credentials["region"] = details["region"]
	credentials["aws_access_key_id"] = details["aws_access_key_id"]
	credentials["aws_secret_access_key"] = details["aws_secret_access_key"]

	return &credentials
}

func mapComponents(d *def.Definition, g *graph.Graph) error {
	// Map basic component values from definition

	for _, vpc := range MapVpcs(d) {
		err := g.AddComponent(vpc)
		if err != nil {
			return err
		}
	}

	for _, gateway := range MapInternetGateways(d) {
		err := g.AddComponent(gateway)
		if err != nil {
			return err
		}
	}

	for _, network := range MapNetworks(d) {
		err := g.AddComponent(network)
		if err != nil {
			return err
		}
	}

	for _, instance := range MapInstances(d) {
		err := g.AddComponent(instance)
		if err != nil {
			return err
		}
	}

	for _, securitygroup := range MapSecurityGroups(d) {
		err := g.AddComponent(securitygroup)
		if err != nil {
			return err
		}
	}

	for _, elb := range MapELBs(d) {
		err := g.AddComponent(elb)
		if err != nil {
			return err
		}
	}

	for _, ebs := range MapEBSVolumes(d) {
		err := g.AddComponent(ebs)
		if err != nil {
			return err
		}
	}

	for _, nat := range MapNats(d) {
		err := g.AddComponent(nat)
		if err != nil {
			return err
		}
	}

	for _, rds := range MapRDSClusters(d) {
		err := g.AddComponent(rds)
		if err != nil {
			return err
		}
	}

	for _, rds := range MapRDSInstances(d) {
		err := g.AddComponent(rds)
		if err != nil {
			return err
		}
	}

	for _, s3 := range MapS3Buckets(d) {
		err := g.AddComponent(s3)
		if err != nil {
			return err
		}
	}

	for _, route53 := range MapRoute53Zones(d) {
		err := g.AddComponent(route53)
		if err != nil {
			return err
		}
	}

	return nil
}

func mapTags(name, service string) map[string]string {
	tags := make(map[string]string)

	tags["Name"] = name
	tags["ernest.service"] = service

	return tags
}

func mapTagsServiceOnly(service string) map[string]string {
	tags := make(map[string]string)

	tags["ernest.service"] = service

	return tags
}
<commit_msg>fixed supported types<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mapper

import (
	"errors"

	"github.com/ernestio/definition-mapper/libmapper"
	"github.com/ernestio/definition-mapper/libmapper/providers/aws/components"
	def "github.com/ernestio/definition-mapper/libmapper/providers/aws/definition"
	"github.com/mitchellh/mapstructure"
	graph "gopkg.in/r3labs/graph.v2"
)

// SUPPORTEDCOMPONENTS represents all component types supported by ernest
var SUPPORTEDCOMPONENTS = []string{"vpc", "internet_gateway", "network", "instance", "firewall", "nat", "elb", "ebs_volume", "s3", "route53", "rds_instance", "rds_cluster"}

// Mapper : implements the generic mapper structure
type Mapper struct{}

// New : returns a new aws mapper
func New() libmapper.Mapper {
	return &Mapper{}
}

// ConvertDefinition : converts the input yaml definition to a graph format
func (m Mapper) ConvertDefinition(gd libmapper.Definition) (*graph.Graph, error) {
	g := graph.New()

	d, ok := gd.(*def.Definition)
	if ok != true {
		return g, errors.New("Could not convert generic definition into aws format")
	}

	// Map basic component values from definition
	err := mapComponents(d, g)
	if err != nil {
		return g, err
	}

	for _, c := range g.Components {
		c.Rebuild(g)

		// Validate Components
		err := c.Validate()
		if err != nil {
			return g, err
		}

		// Build internal & template values
		for _, dep := range c.Dependencies() {
			if g.HasComponent(dep) != true {
				return g, errors.New("Could not resolve component dependency: " + dep)
			}
		}

		// Build dependencies
		for _, dep := range c.Dependencies() {
			g.Connect(dep, c.GetID())
		}
	}

	return g, nil
}

// ConvertGraph : converts the service graph into an input yaml format
func (m Mapper) ConvertGraph(g *graph.Graph) (libmapper.Definition, error) {
	var d def.Definition

	for _, c := range g.Components {
		c.Rebuild(g)

		for _, dep := range c.Dependencies() {
			if g.HasComponent(dep) != true {
				return g, errors.New("Could not resolve component dependency: " + dep)
			}
		}

		err := c.Validate()
		if err != nil {
			return d, err
		}
	}

	d.Vpcs = MapDefinitionVpcs(g)
	d.Networks = MapDefinitionNetworks(g)
	d.Instances = MapDefinitionInstances(g)
	d.SecurityGroups = MapDefinitionSecurityGroups(g)
	d.ELBs = MapDefinitionELBs(g)
	d.EBSVolumes = MapDefinitionEBSVolumes(g)
	d.NatGateways = MapDefinitionNats(g)
	d.RDSClusters = MapDefinitionRDSClusters(g)
	d.RDSInstances = MapDefinitionRDSInstances(g)
	d.Route53Zones = MapDefinitionRoute53Zones(g)
	d.S3Buckets = MapDefinitionS3Buckets(g)

	return d, nil
}

// LoadDefinition : returns an aws type definition
func (m Mapper) LoadDefinition(gd map[string]interface{}) (libmapper.Definition, error) {
	var d def.Definition

	err := d.LoadMap(gd)

	return &d, err
}

// LoadGraph : returns a generic interal graph
func (m Mapper) LoadGraph(gg map[string]interface{}) (*graph.Graph, error) {
	g := graph.New()

	g.Load(gg)

	for i := 0; i < len(g.Components); i++ {
		gc := g.Components[i].(*graph.GenericComponent)

		var c graph.Component

		switch gc.GetType() {
		case "vpc":
			c = &components.Vpc{}
		case "network":
			c = &components.Network{}
		case "internet_gateway":
			c = &components.InternetGateway{}
		case "instance":
			c = &components.Instance{}
		case "firewall":
			c = &components.SecurityGroup{}
		case "elb":
			c = &components.ELB{}
		case "ebs_volume":
			c = &components.EBSVolume{}
		case "nat":
			c = &components.NatGateway{}
		case "rds_cluster":
			c = &components.RDSCluster{}
		case "rds_instance":
			c = &components.RDSInstance{}
		case "route53":
			c = &components.Route53Zone{}
		case "s3":
			c = &components.S3Bucket{}
		default:
			continue
		}

		config := &mapstructure.DecoderConfig{
			Metadata: nil,
			Result:   c,
			TagName:  "json",
		}

		decoder, err := mapstructure.NewDecoder(config)
		if err != nil {
			return g, err
		}

		err = decoder.Decode(gc)
		if err != nil {
			return g, err
		}

		g.Components[i] = c
	}

	return g, nil
}

// CreateImportGraph : creates a new graph with component queries used to import components from a provider
func (m Mapper) CreateImportGraph(params []string) *graph.Graph {
	g := graph.New()
	filter := make(map[string]string)

	if len(params) > 0 {
		filter["ernest.service"] = params[0]
	}

	for _, ctype := range SUPPORTEDCOMPONENTS {
		q := MapQuery(ctype+"s", filter)
		g.AddComponent(q)
	}

	return g
}

// ProviderCredentials : maps aws credentials to a generic component
func (m Mapper) ProviderCredentials(details map[string]interface{}) graph.Component {
	credentials := make(graph.GenericComponent)

	credentials["_action"] = "none"
	credentials["_component"] = "credentials"
	credentials["_component_id"] = "credentials::aws"
	credentials["_provider"] = details["type"]
	credentials["name"] = details["name"]
	credentials["region"] = details["region"]
	credentials["aws_access_key_id"] = details["aws_access_key_id"]
	credentials["aws_secret_access_key"] = details["aws_secret_access_key"]

	return &credentials
}

func mapComponents(d *def.Definition, g *graph.Graph) error {
	// Map basic component values from definition

	for _, vpc := range MapVpcs(d) {
		err := g.AddComponent(vpc)
		if err != nil {
			return err
		}
	}

	for _, gateway := range MapInternetGateways(d) {
		err := g.AddComponent(gateway)
		if err != nil {
			return err
		}
	}

	for _, network := range MapNetworks(d) {
		err := g.AddComponent(network)
		if err != nil {
			return err
		}
	}

	for _, instance := range MapInstances(d) {
		err := g.AddComponent(instance)
		if err != nil {
			return err
		}
	}

	for _, securitygroup := range MapSecurityGroups(d) {
		err := g.AddComponent(securitygroup)
		if err != nil {
			return err
		}
	}

	for _, elb := range MapELBs(d) {
		err := g.AddComponent(elb)
		if err != nil {
			return err
		}
	}

	for _, ebs := range MapEBSVolumes(d) {
		err := g.AddComponent(ebs)
		if err != nil {
			return err
		}
	}

	for _, nat := range MapNats(d) {
		err := g.AddComponent(nat)
		if err != nil {
			return err
		}
	}

	for _, rds := range MapRDSClusters(d) {
		err := g.AddComponent(rds)
		if err != nil {
			return err
		}
	}

	for _, rds := range MapRDSInstances(d) {
		err := g.AddComponent(rds)
		if err != nil {
			return err
		}
	}

	for _, s3 := range MapS3Buckets(d) {
		err := g.AddComponent(s3)
		if err != nil {
			return err
		}
	}

	for _, route53 := range MapRoute53Zones(d) {
		err := g.AddComponent(route53)
		if err != nil {
			return err
		}
	}

	return nil
}

func mapTags(name, service string) map[string]string {
	tags := make(map[string]string)

	tags["Name"] = name
	tags["ernest.service"] = service

	return tags
}

func mapTagsServiceOnly(service string) map[string]string {
	tags := make(map[string]string)

	tags["ernest.service"] = service

	return tags
}
<|endoftext|>


<commit_before>package geojson

import (
	_"fmt"
	"github.com/dhconnelly/rtreego"
	"github.com/jeffail/gabs"
	"io/ioutil"
)

// See also
// https://github.com/dhconnelly/rtreego#storing-updating-and-deleting-objects

type WOFBounds struct {
     where *rtreego.Rect
     Id int
     Placetype string
}

func (b WOFBounds) Bounds() *rtreego.Rect {
     return b.where
}

/*
Something like this using "github.com/paulmach/go.geojson" seems
like it would be a good thing but I don't think I have the stamina
to figure out how to parse the properties separately right now...
(20151005/thisisaaronland)
/*

type WOFProperties struct {
     Raw    []byte
     Parsed *gabs.Container
}

type WOFFeature struct {
     ID          json.Number            `json:"id,omitempty"`
     Type        string                 `json:"type"`
     BoundingBox []float64              `json:"bbox,omitempty"`	// maybe make this a WOFBounds (rtree) like properties?
     Geometry    *gj.Geometry      `json:"geometry"`
     Properties  WOFProperties			`json:"properties"`
     // Properties  map[string]interface{} `json:"properties"`
     CRS         map[string]interface{} `json:"crs,omitempty"` // Coordinate Reference System Objects are not currently supported
}
*/

type WOFFeature struct {
	Raw    []byte
	Parsed *gabs.Container
}

func (wof WOFFeature) Body() *gabs.Container {
	return wof.Parsed
}

func (wof WOFFeature) Dumps() string {
	return wof.Parsed.String()
}

func (wof WOFFeature) Id() int {

	body := wof.Body()

	var flid float64
	flid = body.Path("properties.wof:id").Data().(float64)

	id := int(flid)
	return id
}

// Should return a full-on WOFPlacetype object thing-y
// (20151012/thisisaaronland)

func (wof WOFFeature) Placetype() placetype {

	body := wof.Body()

	var placetype string
	placetype = body.Path("properties.wof:placetype").Data().(string)

	return placetype
}

// See notes above in WOFFeature.BoundingBox - for now this will do...
// (20151012/thisisaaronland)

func (wof WOFFeature) Bounds() (*WOFBounds, error) {

	id := wof.Id()
	placetype = wof.Placetype()

	body := wof.Body()

	var swlon float64
	var swlat float64
	var nelon float64
	var nelat float64

	children, _ := body.S("bbox").Children()

	swlon = children[0].Data().(float64)
	swlat = children[1].Data().(float64)
	nelon = children[2].Data().(float64)
	nelat = children[3].Data().(float64)

	llat := nelat - swlat
	llon := nelon - swlon

	// fmt.Printf("%f - %f = %f\n", nelat, swlat, llat)
	// fmt.Printf("%f - %f = %f\n", nelon, swlon, llon)

	pt := rtreego.Point{swlon, swlat}
	rect, err := rtreego.NewRect(pt, []float64{llon, llat})

	if err != nil {
		return nil, err
	}

	return &WOFBounds{rect, id, placetype}, nil
}

func UnmarshalFile(path string) (*WOFFeature, error) {

	body, read_err := ioutil.ReadFile(path)

	if read_err != nil {
		return nil, read_err
	}

	return UnmarshalFeature(body)
}

func UnmarshalFeature(raw []byte) (*WOFFeature, error) {

	parsed, parse_err := gabs.ParseJSON(raw)

	if parse_err != nil {
		return nil, parse_err
	}

	rsp := WOFFeature{
		Raw:    raw,
		Parsed: parsed,
	}

	return &rsp, nil
}
<commit_msg>placetype is not a struct yet<commit_after>package geojson

import (
	_"fmt"
	"github.com/dhconnelly/rtreego"
	"github.com/jeffail/gabs"
	"io/ioutil"
)

// See also
// https://github.com/dhconnelly/rtreego#storing-updating-and-deleting-objects

type WOFBounds struct {
     where *rtreego.Rect
     Id int
     Placetype string
}

func (b WOFBounds) Bounds() *rtreego.Rect {
     return b.where
}

/*
Something like this using "github.com/paulmach/go.geojson" seems
like it would be a good thing but I don't think I have the stamina
to figure out how to parse the properties separately right now...
(20151005/thisisaaronland)
/*

type WOFProperties struct {
     Raw    []byte
     Parsed *gabs.Container
}

type WOFFeature struct {
     ID          json.Number            `json:"id,omitempty"`
     Type        string                 `json:"type"`
     BoundingBox []float64              `json:"bbox,omitempty"`	// maybe make this a WOFBounds (rtree) like properties?
     Geometry    *gj.Geometry      `json:"geometry"`
     Properties  WOFProperties			`json:"properties"`
     // Properties  map[string]interface{} `json:"properties"`
     CRS         map[string]interface{} `json:"crs,omitempty"` // Coordinate Reference System Objects are not currently supported
}
*/

type WOFFeature struct {
	Raw    []byte
	Parsed *gabs.Container
}

func (wof WOFFeature) Body() *gabs.Container {
	return wof.Parsed
}

func (wof WOFFeature) Dumps() string {
	return wof.Parsed.String()
}

func (wof WOFFeature) Id() int {

	body := wof.Body()

	var flid float64
	flid = body.Path("properties.wof:id").Data().(float64)

	id := int(flid)
	return id
}

// Should return a full-on WOFPlacetype object thing-y
// (20151012/thisisaaronland)

func (wof WOFFeature) Placetype() string {

	body := wof.Body()

	var placetype string
	placetype = body.Path("properties.wof:placetype").Data().(string)

	return placetype
}

// See notes above in WOFFeature.BoundingBox - for now this will do...
// (20151012/thisisaaronland)

func (wof WOFFeature) Bounds() (*WOFBounds, error) {

	id := wof.Id()
	placetype = wof.Placetype()

	body := wof.Body()

	var swlon float64
	var swlat float64
	var nelon float64
	var nelat float64

	children, _ := body.S("bbox").Children()

	swlon = children[0].Data().(float64)
	swlat = children[1].Data().(float64)
	nelon = children[2].Data().(float64)
	nelat = children[3].Data().(float64)

	llat := nelat - swlat
	llon := nelon - swlon

	// fmt.Printf("%f - %f = %f\n", nelat, swlat, llat)
	// fmt.Printf("%f - %f = %f\n", nelon, swlon, llon)

	pt := rtreego.Point{swlon, swlat}
	rect, err := rtreego.NewRect(pt, []float64{llon, llat})

	if err != nil {
		return nil, err
	}

	return &WOFBounds{rect, id, placetype}, nil
}

func UnmarshalFile(path string) (*WOFFeature, error) {

	body, read_err := ioutil.ReadFile(path)

	if read_err != nil {
		return nil, read_err
	}

	return UnmarshalFeature(body)
}

func UnmarshalFeature(raw []byte) (*WOFFeature, error) {

	parsed, parse_err := gabs.ParseJSON(raw)

	if parse_err != nil {
		return nil, parse_err
	}

	rsp := WOFFeature{
		Raw:    raw,
		Parsed: parsed,
	}

	return &rsp, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"gesture/plugin"
	"gesture/plugin/identity"
	"gesture/plugin/gis"
	"gesture/plugin/twitter"
	"gesture/rewrite"
	irc "github.com/fluffle/goirc/client"
	"log"
	"os"
	"strings"
	"io/ioutil"
)

var (
	plugins  []plugin.Plugin
)

// gesture config
type Config struct {
	BotName  string
	Hostname string
	SSL bool
	Channels []string
}

// readsConfig unmarshals the config from a file and returns the struct
func readConfig(filename string) (*Config, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	var config Config
	b, err := ioutil.ReadAll(file)
	if err != nil {
		return nil, err
	}
	if err = json.Unmarshal(b, &config); err != nil {
		return nil, err
	}
	return &config, nil

}

// a Plugin is something that can respond to messages
func main() {
	if len(os.Args) < 2 {
		log.Println("usage: gesture [conf_file]")
		os.Exit(1)
	}

	config, err := readConfig(os.Args[1])
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	plugins = []plugin.Plugin{
		twitter.NewPlugin(),
		gis.NewPlugin(),
		identity.NewPlugin(config.BotName),
	}

	flag.Parse()
	c := irc.SimpleClient(config.BotName)
	c.SSL = config.SSL
	c.AddHandler(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			for _, channel := range config.Channels {
				conn.Join(channel)
			}
		})
	quit := make(chan bool)
	c.AddHandler(irc.DISCONNECTED, func(conn *irc.Conn, line *irc.Line) { quit <- true })
	c.AddHandler("PRIVMSG", func(conn *irc.Conn, line *irc.Line) {
		messageReceived(conn, line)
	})
	if err := c.Connect(config.Hostname); err != nil {
		log.Fatalf("Connection error: %s\n", err)
	}
	// Wait for disconnect
	<-quit
}

// When a message comes in on a channel gesture has joined, this method will be called.
func messageReceived(conn *irc.Conn, line *irc.Line) {
	if len(line.Args) > 1 {
		channel := line.Args[0]
		message := line.Args[1]

		mc := &messageContext{conn, line}

		log.Printf(">> %s (%s): %s\n", line.Nick, channel, message)

		handled := false
		for _, plugin := range plugins {
			success, err := plugin.Call(mc)
			if err != nil {
				log.Print(err)
			}
			if success {
				handled = true
				break
			}
		}
		if !handled {
			// try to expand any links
			for _, token := range rewrite.GetRewrittenLinks(mc.Message()) {
				mc.Reply(token)
			}
		}
	}
}

// when an error occurs, calling this method will send the error back to the irc channel
func sendError(conn *irc.Conn, channel string, nick string, err error) {
	log.Print(err)
	conn.Privmsg(channel, fmt.Sprintf("%s: oops: %v", nick, err))
}

type messageContext struct {
	conn *irc.Conn
	line *irc.Line
}

func (mc *messageContext) Message() string {
	if len(mc.line.Args) > 1 {
		return mc.line.Args[1]
	}
	return ""
}

func (mc *messageContext) Command() string {
	sliced := strings.Split(mc.Message(), " ")
	return sliced[0]
}

func (mc *messageContext) CommandArgs() []string {
	sliced := strings.Split(mc.Message(), " ")
	return sliced[1:]
}

func (mc *messageContext) Reply(message string) {
	channel := mc.line.Args[0]
	mc.conn.Privmsg(channel, fmt.Sprintf("%s: ftfy -> %s", mc.line.Nick, rewrite.Rewrite(message)))
}

func (mc *messageContext) Send(message string) {
	channel := mc.line.Args[0]
	mc.conn.Privmsg(channel, rewrite.Rewrite(message))
}
<commit_msg>Logging around connect and channel join events<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"gesture/plugin"
	"gesture/plugin/identity"
	"gesture/plugin/gis"
	"gesture/plugin/twitter"
	"gesture/rewrite"
	irc "github.com/fluffle/goirc/client"
	"log"
	"os"
	"strings"
	"io/ioutil"
)

var (
	plugins  []plugin.Plugin
)

// gesture config
type Config struct {
	BotName  string
	Hostname string
	SSL bool
	Channels []string
}

// readsConfig unmarshals the config from a file and returns the struct
func readConfig(filename string) (*Config, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	var config Config
	b, err := ioutil.ReadAll(file)
	if err != nil {
		return nil, err
	}
	if err = json.Unmarshal(b, &config); err != nil {
		return nil, err
	}
	return &config, nil

}

// a Plugin is something that can respond to messages
func main() {
	if len(os.Args) < 2 {
		log.Println("usage: gesture [conf_file]")
		os.Exit(1)
	}

	config, err := readConfig(os.Args[1])
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	plugins = []plugin.Plugin{
		twitter.NewPlugin(),
		gis.NewPlugin(),
		identity.NewPlugin(config.BotName),
	}

	flag.Parse()
	c := irc.SimpleClient(config.BotName)
	c.SSL = config.SSL
	c.AddHandler(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			log.Println("Connected to", config.Hostname, "!")
			for _, channel := range config.Channels {
				conn.Join(channel)
			}
		})
	quit := make(chan bool)
	c.AddHandler("JOIN", func(conn *irc.Conn, line *irc.Line) {
		if line.Nick == config.BotName {
			log.Printf("Joined %+v\n", line.Args)
		}
	})
	c.AddHandler(irc.DISCONNECTED, func(conn *irc.Conn, line *irc.Line) {
		log.Println("Disconnected. Quitting.")
		quit <- true
	})
	c.AddHandler("PRIVMSG", func(conn *irc.Conn, line *irc.Line) {
		messageReceived(conn, line)
	})
	if err := c.Connect(config.Hostname); err != nil {
		log.Fatalf("Connection error: %s\n", err)
	}
	// Wait for disconnect
	<-quit
}

// When a message comes in on a channel gesture has joined, this method will be called.
func messageReceived(conn *irc.Conn, line *irc.Line) {
	if len(line.Args) > 1 {
		channel := line.Args[0]
		message := line.Args[1]

		mc := &messageContext{conn, line}

		log.Printf(">> %s (%s): %s\n", line.Nick, channel, message)

		handled := false
		for _, plugin := range plugins {
			success, err := plugin.Call(mc)
			if err != nil {
				log.Print(err)
			}
			if success {
				handled = true
				break
			}
		}
		if !handled {
			// try to expand any links
			for _, token := range rewrite.GetRewrittenLinks(mc.Message()) {
				mc.Reply(token)
			}
		}
	}
}

// when an error occurs, calling this method will send the error back to the irc channel
func sendError(conn *irc.Conn, channel string, nick string, err error) {
	log.Print(err)
	conn.Privmsg(channel, fmt.Sprintf("%s: oops: %v", nick, err))
}

type messageContext struct {
	conn *irc.Conn
	line *irc.Line
}

func (mc *messageContext) Message() string {
	if len(mc.line.Args) > 1 {
		return mc.line.Args[1]
	}
	return ""
}

func (mc *messageContext) Command() string {
	sliced := strings.Split(mc.Message(), " ")
	return sliced[0]
}

func (mc *messageContext) CommandArgs() []string {
	sliced := strings.Split(mc.Message(), " ")
	return sliced[1:]
}

func (mc *messageContext) Reply(message string) {
	channel := mc.line.Args[0]
	mc.conn.Privmsg(channel, fmt.Sprintf("%s: ftfy -> %s", mc.line.Nick, rewrite.Rewrite(message)))
}

func (mc *messageContext) Send(message string) {
	channel := mc.line.Args[0]
	mc.conn.Privmsg(channel, rewrite.Rewrite(message))
}
<|endoftext|>


<commit_before>package exoscale

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"strings"
	"time"

	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/mcnflag"
	"github.com/docker/machine/libmachine/mcnutils"
	"github.com/docker/machine/libmachine/state"
	"github.com/pyr/egoscale/src/egoscale"
)

type Driver struct {
	*drivers.BaseDriver
	URL              string
	APIKey           string `json:"ApiKey"`
	APISecretKey     string `json:"ApiSecretKey"`
	InstanceProfile  string
	DiskSize         int
	Image            string
	SecurityGroup    string
	AvailabilityZone string
	KeyPair          string
	PublicKey        string
	UserDataFile     string
	ID               string `json:"Id"`
}

const (
	defaultInstanceProfile  = "small"
	defaultDiskSize         = 50
	defaultImage            = "ubuntu-15.10"
	defaultAvailabilityZone = "ch-gva-2"
	defaultSSHUser          = "ubuntu"
)

// GetCreateFlags registers the flags this driver adds to
// "docker hosts create"
func (d *Driver) GetCreateFlags() []mcnflag.Flag {
	return []mcnflag.Flag{
		mcnflag.StringFlag{
			EnvVar: "EXOSCALE_ENDPOINT",
			Name:   "exoscale-url",
			Usage:  "exoscale API endpoint",
		},
		mcnflag.StringFlag{
			EnvVar: "EXOSCALE_API_KEY",
			Name:   "exoscale-api-key",
			Usage:  "exoscale API key",
		},
		mcnflag.StringFlag{
			EnvVar: "EXOSCALE_API_SECRET",
			Name:   "exoscale-api-secret-key",
			Usage:  "exoscale API secret key",
		},
		mcnflag.StringFlag{
			EnvVar: "EXOSCALE_INSTANCE_PROFILE",
			Name:   "exoscale-instance-profile",
			Value:  defaultInstanceProfile,
			Usage:  "exoscale instance profile (small, medium, large, ...)",
		},
		mcnflag.IntFlag{
			EnvVar: "EXOSCALE_DISK_SIZE",
			Name:   "exoscale-disk-size",
			Value:  defaultDiskSize,
			Usage:  "exoscale disk size (10, 50, 100, 200, 400)",
		},
		mcnflag.StringFlag{
			EnvVar: "EXSOCALE_IMAGE",
			Name:   "exoscale-image",
			Value:  defaultImage,
			Usage:  "exoscale image template",
		},
		mcnflag.StringSliceFlag{
			EnvVar: "EXOSCALE_SECURITY_GROUP",
			Name:   "exoscale-security-group",
			Value:  []string{},
			Usage:  "exoscale security group",
		},
		mcnflag.StringFlag{
			EnvVar: "EXOSCALE_AVAILABILITY_ZONE",
			Name:   "exoscale-availability-zone",
			Value:  defaultAvailabilityZone,
			Usage:  "exoscale availibility zone",
		},
		mcnflag.StringFlag{
			EnvVar: "EXOSCALE_SSH_USER",
			Name:   "exoscale-ssh-user",
			Value:  defaultSSHUser,
			Usage:  "Set the name of the ssh user",
		},
		mcnflag.StringFlag{
			EnvVar: "EXOSCALE_USERDATA",
			Name:   "exoscale-userdata",
			Usage:  "path to file with cloud-init user-data",
		},
	}
}

func NewDriver(hostName, storePath string) drivers.Driver {
	return &Driver{
		InstanceProfile:  defaultInstanceProfile,
		DiskSize:         defaultDiskSize,
		Image:            defaultImage,
		AvailabilityZone: defaultAvailabilityZone,
		BaseDriver: &drivers.BaseDriver{
			MachineName: hostName,
			StorePath:   storePath,
		},
	}
}

func (d *Driver) GetSSHHostname() (string, error) {
	return d.GetIP()
}

func (d *Driver) GetSSHUsername() string {
	if d.SSHUser == "" {
		d.SSHUser = defaultSSHUser
	}

	return d.SSHUser
}

// DriverName returns the name of the driver
func (d *Driver) DriverName() string {
	return "exoscale"
}

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
	d.URL = flags.String("exoscale-url")
	d.APIKey = flags.String("exoscale-api-key")
	d.APISecretKey = flags.String("exoscale-api-secret-key")
	d.InstanceProfile = flags.String("exoscale-instance-profile")
	d.DiskSize = flags.Int("exoscale-disk-size")
	d.Image = flags.String("exoscale-image")
	securityGroups := flags.StringSlice("exoscale-security-group")
	if len(securityGroups) == 0 {
		securityGroups = []string{"docker-machine"}
	}
	d.SecurityGroup = strings.Join(securityGroups, ",")
	d.AvailabilityZone = flags.String("exoscale-availability-zone")
	d.SSHUser = flags.String("exoscale-ssh-user")
	d.UserDataFile = flags.String("exoscale-userdata")
	d.SetSwarmConfigFromFlags(flags)

	if d.URL == "" {
		d.URL = "https://api.exoscale.ch/compute"
	}
	if d.APIKey == "" || d.APISecretKey == "" {
		return fmt.Errorf("Please specify an API key (--exoscale-api-key) and an API secret key (--exoscale-api-secret-key).")
	}

	return nil
}

func (d *Driver) PreCreateCheck() error {
	if d.UserDataFile != "" {
		if _, err := os.Stat(d.UserDataFile); os.IsNotExist(err) {
			return fmt.Errorf("user-data file %s could not be found", d.UserDataFile)
		}
	}

	return nil
}

func (d *Driver) GetURL() (string, error) {
	if err := drivers.MustBeRunning(d); err != nil {
		return "", err
	}

	ip, err := d.GetIP()
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("tcp://%s", net.JoinHostPort(ip, "2376")), nil
}

func (d *Driver) GetState() (state.State, error) {
	client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)
	vm, err := client.GetVirtualMachine(d.ID)
	if err != nil {
		return state.Error, err
	}
	switch vm.State {
	case "Starting":
		return state.Starting, nil
	case "Running":
		return state.Running, nil
	case "Stopping":
		return state.Running, nil
	case "Stopped":
		return state.Stopped, nil
	case "Destroyed":
		return state.Stopped, nil
	case "Expunging":
		return state.Stopped, nil
	case "Migrating":
		return state.Paused, nil
	case "Error":
		return state.Error, nil
	case "Unknown":
		return state.Error, nil
	case "Shutdowned":
		return state.Stopped, nil
	}
	return state.None, nil
}

func (d *Driver) createDefaultSecurityGroup(client *egoscale.Client, group string) (string, error) {
	rules := []egoscale.SecurityGroupRule{
		{
			SecurityGroupId: "",
			Cidr:            "0.0.0.0/0",
			Protocol:        "TCP",
			Port:            22,
		},
		{
			SecurityGroupId: "",
			Cidr:            "0.0.0.0/0",
			Protocol:        "TCP",
			Port:            2376,
		},
		{
			SecurityGroupId: "",
			Cidr:            "0.0.0.0/0",
			Protocol:        "TCP",
			Port:            3376,
		},
		{
			SecurityGroupId: "",
			Cidr:            "0.0.0.0/0",
			Protocol:        "ICMP",
			IcmpType:        8,
			IcmpCode:        0,
		},
	}
	sgresp, err := client.CreateSecurityGroupWithRules(
		group,
		rules,
		make([]egoscale.SecurityGroupRule, 0, 0))
	if err != nil {
		return "", err
	}
	sg := sgresp.Id
	return sg, nil
}

func (d *Driver) Create() error {
	userdata, err := d.getCloudInit()
	if err != nil {
		return err
	}

	log.Infof("Querying exoscale for the requested parameters...")
	client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)
	topology, err := client.GetTopology()
	if err != nil {
		return err
	}

	// Availability zone UUID
	zone, ok := topology.Zones[d.AvailabilityZone]
	if !ok {
		return fmt.Errorf("Availability zone %v doesn't exist",
			d.AvailabilityZone)
	}
	log.Debugf("Availability zone %v = %s", d.AvailabilityZone, zone)

	// Image UUID
	var tpl string
	images, ok := topology.Images[strings.ToLower(d.Image)]
	if ok {
		tpl, ok = images[d.DiskSize]
	}
	if !ok {
		return fmt.Errorf("Unable to find image %v with size %d",
			d.Image, d.DiskSize)
	}
	log.Debugf("Image %v(%d) = %s", d.Image, d.DiskSize, tpl)

	// Profile UUID
	profile, ok := topology.Profiles[strings.ToLower(d.InstanceProfile)]
	if !ok {
		return fmt.Errorf("Unable to find the %s profile",
			d.InstanceProfile)
	}
	log.Debugf("Profile %v = %s", d.InstanceProfile, profile)

	// Security groups
	securityGroups := strings.Split(d.SecurityGroup, ",")
	sgs := make([]string, len(securityGroups))
	for idx, group := range securityGroups {
		sg, ok := topology.SecurityGroups[group]
		if !ok {
			log.Infof("Security group %v does not exist, create it",
				group)
			sg, err = d.createDefaultSecurityGroup(client, group)
			if err != nil {
				return err
			}
		}
		log.Debugf("Security group %v = %s", group, sg)
		sgs[idx] = sg
	}

	log.Infof("Generate an SSH keypair...")
	keypairName := fmt.Sprintf("docker-machine-%s", d.MachineName)
	kpresp, err := client.CreateKeypair(keypairName)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(d.GetSSHKeyPath(), []byte(kpresp.Privatekey), 0600)
	if err != nil {
		return err
	}
	d.KeyPair = keypairName

	log.Infof("Spawn exoscale host...")
	log.Debugf("Using the following cloud-init file:")
	log.Debugf("%s", userdata)

	machineProfile := egoscale.MachineProfile{
		Template:        tpl,
		ServiceOffering: profile,
		SecurityGroups:  sgs,
		Userdata:        userdata,
		Zone:            zone,
		Keypair:         d.KeyPair,
		Name:            d.MachineName,
	}

	cvmresp, err := client.CreateVirtualMachine(machineProfile)
	if err != nil {
		return err
	}

	vm, err := d.waitForVM(client, cvmresp)
	if err != nil {
		return err
	}
	d.IPAddress = vm.Nic[0].Ipaddress
	d.ID = vm.Id

	return nil
}

func (d *Driver) Start() error {
	client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)

	svmresp, err := client.StartVirtualMachine(d.ID)
	if err != nil {
		return err
	}

	return d.waitForJob(client, svmresp)
}

func (d *Driver) Stop() error {
	client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)

	svmresp, err := client.StopVirtualMachine(d.ID)
	if err != nil {
		return err
	}

	return d.waitForJob(client, svmresp)
}

func (d *Driver) Restart() error {
	client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)

	svmresp, err := client.RebootVirtualMachine(d.ID)
	if err != nil {
		return err
	}

	return d.waitForJob(client, svmresp)
}

func (d *Driver) Kill() error {
	return d.Stop()
}

func (d *Driver) Remove() error {
	client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)

	// Destroy the SSH key
	if _, err := client.DeleteKeypair(d.KeyPair); err != nil {
		return err
	}

	// Destroy the virtual machine
	dvmresp, err := client.DestroyVirtualMachine(d.ID)
	if err != nil {
		return err
	}
	if err = d.waitForJob(client, dvmresp); err != nil {
		return err
	}
	return nil
}

func (d *Driver) jobIsDone(client *egoscale.Client, jobid string) (bool, error) {
	resp, err := client.PollAsyncJob(jobid)
	if err != nil {
		return true, err
	}
	switch resp.Jobstatus {
	case 0: // Job is still in progress
	case 1: // Job has successfully completed
		return true, nil
	case 2: // Job has failed to complete
		return true, fmt.Errorf("Operation failed to complete")
	default: // Some other code
	}
	return false, nil
}

func (d *Driver) waitForJob(client *egoscale.Client, jobid string) error {
	log.Infof("Waiting for job to complete...")
	return mcnutils.WaitForSpecificOrError(func() (bool, error) {
		return d.jobIsDone(client, jobid)
	}, 60, 2*time.Second)
}

func (d *Driver) waitForVM(client *egoscale.Client, jobid string) (*egoscale.DeployVirtualMachineResponse, error) {
	if err := d.waitForJob(client, jobid); err != nil {
		return nil, err
	}
	resp, err := client.PollAsyncJob(jobid)
	if err != nil {
		return nil, err
	}
	vm, err := client.AsyncToVirtualMachine(*resp)
	if err != nil {
		return nil, err
	}

	return vm, nil
}

// Build a cloud-init user data string that will install and run
// docker.
func (d *Driver) getCloudInit() (string, error) {
	if d.UserDataFile != "" {
		buf, err := ioutil.ReadFile(d.UserDataFile)
		if err != nil {
			return "", err
		}
		return string(buf), nil
	}

	return `#cloud-config
manage_etc_hosts: true
`, nil
}
<commit_msg>exoscale: fix cloud-init file to be compatible with CoreOS<commit_after>package exoscale

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"strings"
	"time"

	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/mcnflag"
	"github.com/docker/machine/libmachine/mcnutils"
	"github.com/docker/machine/libmachine/state"
	"github.com/pyr/egoscale/src/egoscale"
)

type Driver struct {
	*drivers.BaseDriver
	URL              string
	APIKey           string `json:"ApiKey"`
	APISecretKey     string `json:"ApiSecretKey"`
	InstanceProfile  string
	DiskSize         int
	Image            string
	SecurityGroup    string
	AvailabilityZone string
	KeyPair          string
	PublicKey        string
	UserDataFile     string
	ID               string `json:"Id"`
}

const (
	defaultInstanceProfile  = "small"
	defaultDiskSize         = 50
	defaultImage            = "ubuntu-15.10"
	defaultAvailabilityZone = "ch-gva-2"
	defaultSSHUser          = "ubuntu"
)

// GetCreateFlags registers the flags this driver adds to
// "docker hosts create"
func (d *Driver) GetCreateFlags() []mcnflag.Flag {
	return []mcnflag.Flag{
		mcnflag.StringFlag{
			EnvVar: "EXOSCALE_ENDPOINT",
			Name:   "exoscale-url",
			Usage:  "exoscale API endpoint",
		},
		mcnflag.StringFlag{
			EnvVar: "EXOSCALE_API_KEY",
			Name:   "exoscale-api-key",
			Usage:  "exoscale API key",
		},
		mcnflag.StringFlag{
			EnvVar: "EXOSCALE_API_SECRET",
			Name:   "exoscale-api-secret-key",
			Usage:  "exoscale API secret key",
		},
		mcnflag.StringFlag{
			EnvVar: "EXOSCALE_INSTANCE_PROFILE",
			Name:   "exoscale-instance-profile",
			Value:  defaultInstanceProfile,
			Usage:  "exoscale instance profile (small, medium, large, ...)",
		},
		mcnflag.IntFlag{
			EnvVar: "EXOSCALE_DISK_SIZE",
			Name:   "exoscale-disk-size",
			Value:  defaultDiskSize,
			Usage:  "exoscale disk size (10, 50, 100, 200, 400)",
		},
		mcnflag.StringFlag{
			EnvVar: "EXSOCALE_IMAGE",
			Name:   "exoscale-image",
			Value:  defaultImage,
			Usage:  "exoscale image template",
		},
		mcnflag.StringSliceFlag{
			EnvVar: "EXOSCALE_SECURITY_GROUP",
			Name:   "exoscale-security-group",
			Value:  []string{},
			Usage:  "exoscale security group",
		},
		mcnflag.StringFlag{
			EnvVar: "EXOSCALE_AVAILABILITY_ZONE",
			Name:   "exoscale-availability-zone",
			Value:  defaultAvailabilityZone,
			Usage:  "exoscale availibility zone",
		},
		mcnflag.StringFlag{
			EnvVar: "EXOSCALE_SSH_USER",
			Name:   "exoscale-ssh-user",
			Value:  defaultSSHUser,
			Usage:  "Set the name of the ssh user",
		},
		mcnflag.StringFlag{
			EnvVar: "EXOSCALE_USERDATA",
			Name:   "exoscale-userdata",
			Usage:  "path to file with cloud-init user-data",
		},
	}
}

func NewDriver(hostName, storePath string) drivers.Driver {
	return &Driver{
		InstanceProfile:  defaultInstanceProfile,
		DiskSize:         defaultDiskSize,
		Image:            defaultImage,
		AvailabilityZone: defaultAvailabilityZone,
		BaseDriver: &drivers.BaseDriver{
			MachineName: hostName,
			StorePath:   storePath,
		},
	}
}

func (d *Driver) GetSSHHostname() (string, error) {
	return d.GetIP()
}

func (d *Driver) GetSSHUsername() string {
	if d.SSHUser == "" {
		d.SSHUser = defaultSSHUser
	}

	return d.SSHUser
}

// DriverName returns the name of the driver
func (d *Driver) DriverName() string {
	return "exoscale"
}

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
	d.URL = flags.String("exoscale-url")
	d.APIKey = flags.String("exoscale-api-key")
	d.APISecretKey = flags.String("exoscale-api-secret-key")
	d.InstanceProfile = flags.String("exoscale-instance-profile")
	d.DiskSize = flags.Int("exoscale-disk-size")
	d.Image = flags.String("exoscale-image")
	securityGroups := flags.StringSlice("exoscale-security-group")
	if len(securityGroups) == 0 {
		securityGroups = []string{"docker-machine"}
	}
	d.SecurityGroup = strings.Join(securityGroups, ",")
	d.AvailabilityZone = flags.String("exoscale-availability-zone")
	d.SSHUser = flags.String("exoscale-ssh-user")
	d.UserDataFile = flags.String("exoscale-userdata")
	d.SetSwarmConfigFromFlags(flags)

	if d.URL == "" {
		d.URL = "https://api.exoscale.ch/compute"
	}
	if d.APIKey == "" || d.APISecretKey == "" {
		return fmt.Errorf("Please specify an API key (--exoscale-api-key) and an API secret key (--exoscale-api-secret-key).")
	}

	return nil
}

func (d *Driver) PreCreateCheck() error {
	if d.UserDataFile != "" {
		if _, err := os.Stat(d.UserDataFile); os.IsNotExist(err) {
			return fmt.Errorf("user-data file %s could not be found", d.UserDataFile)
		}
	}

	return nil
}

func (d *Driver) GetURL() (string, error) {
	if err := drivers.MustBeRunning(d); err != nil {
		return "", err
	}

	ip, err := d.GetIP()
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("tcp://%s", net.JoinHostPort(ip, "2376")), nil
}

func (d *Driver) GetState() (state.State, error) {
	client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)
	vm, err := client.GetVirtualMachine(d.ID)
	if err != nil {
		return state.Error, err
	}
	switch vm.State {
	case "Starting":
		return state.Starting, nil
	case "Running":
		return state.Running, nil
	case "Stopping":
		return state.Running, nil
	case "Stopped":
		return state.Stopped, nil
	case "Destroyed":
		return state.Stopped, nil
	case "Expunging":
		return state.Stopped, nil
	case "Migrating":
		return state.Paused, nil
	case "Error":
		return state.Error, nil
	case "Unknown":
		return state.Error, nil
	case "Shutdowned":
		return state.Stopped, nil
	}
	return state.None, nil
}

func (d *Driver) createDefaultSecurityGroup(client *egoscale.Client, group string) (string, error) {
	rules := []egoscale.SecurityGroupRule{
		{
			SecurityGroupId: "",
			Cidr:            "0.0.0.0/0",
			Protocol:        "TCP",
			Port:            22,
		},
		{
			SecurityGroupId: "",
			Cidr:            "0.0.0.0/0",
			Protocol:        "TCP",
			Port:            2376,
		},
		{
			SecurityGroupId: "",
			Cidr:            "0.0.0.0/0",
			Protocol:        "TCP",
			Port:            3376,
		},
		{
			SecurityGroupId: "",
			Cidr:            "0.0.0.0/0",
			Protocol:        "ICMP",
			IcmpType:        8,
			IcmpCode:        0,
		},
	}
	sgresp, err := client.CreateSecurityGroupWithRules(
		group,
		rules,
		make([]egoscale.SecurityGroupRule, 0, 0))
	if err != nil {
		return "", err
	}
	sg := sgresp.Id
	return sg, nil
}

func (d *Driver) Create() error {
	userdata, err := d.getCloudInit()
	if err != nil {
		return err
	}

	log.Infof("Querying exoscale for the requested parameters...")
	client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)
	topology, err := client.GetTopology()
	if err != nil {
		return err
	}

	// Availability zone UUID
	zone, ok := topology.Zones[d.AvailabilityZone]
	if !ok {
		return fmt.Errorf("Availability zone %v doesn't exist",
			d.AvailabilityZone)
	}
	log.Debugf("Availability zone %v = %s", d.AvailabilityZone, zone)

	// Image UUID
	var tpl string
	images, ok := topology.Images[strings.ToLower(d.Image)]
	if ok {
		tpl, ok = images[d.DiskSize]
	}
	if !ok {
		return fmt.Errorf("Unable to find image %v with size %d",
			d.Image, d.DiskSize)
	}
	log.Debugf("Image %v(%d) = %s", d.Image, d.DiskSize, tpl)

	// Profile UUID
	profile, ok := topology.Profiles[strings.ToLower(d.InstanceProfile)]
	if !ok {
		return fmt.Errorf("Unable to find the %s profile",
			d.InstanceProfile)
	}
	log.Debugf("Profile %v = %s", d.InstanceProfile, profile)

	// Security groups
	securityGroups := strings.Split(d.SecurityGroup, ",")
	sgs := make([]string, len(securityGroups))
	for idx, group := range securityGroups {
		sg, ok := topology.SecurityGroups[group]
		if !ok {
			log.Infof("Security group %v does not exist, create it",
				group)
			sg, err = d.createDefaultSecurityGroup(client, group)
			if err != nil {
				return err
			}
		}
		log.Debugf("Security group %v = %s", group, sg)
		sgs[idx] = sg
	}

	log.Infof("Generate an SSH keypair...")
	keypairName := fmt.Sprintf("docker-machine-%s", d.MachineName)
	kpresp, err := client.CreateKeypair(keypairName)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(d.GetSSHKeyPath(), []byte(kpresp.Privatekey), 0600)
	if err != nil {
		return err
	}
	d.KeyPair = keypairName

	log.Infof("Spawn exoscale host...")
	log.Debugf("Using the following cloud-init file:")
	log.Debugf("%s", userdata)

	machineProfile := egoscale.MachineProfile{
		Template:        tpl,
		ServiceOffering: profile,
		SecurityGroups:  sgs,
		Userdata:        userdata,
		Zone:            zone,
		Keypair:         d.KeyPair,
		Name:            d.MachineName,
	}

	cvmresp, err := client.CreateVirtualMachine(machineProfile)
	if err != nil {
		return err
	}

	vm, err := d.waitForVM(client, cvmresp)
	if err != nil {
		return err
	}
	d.IPAddress = vm.Nic[0].Ipaddress
	d.ID = vm.Id

	return nil
}

func (d *Driver) Start() error {
	client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)

	svmresp, err := client.StartVirtualMachine(d.ID)
	if err != nil {
		return err
	}

	return d.waitForJob(client, svmresp)
}

func (d *Driver) Stop() error {
	client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)

	svmresp, err := client.StopVirtualMachine(d.ID)
	if err != nil {
		return err
	}

	return d.waitForJob(client, svmresp)
}

func (d *Driver) Restart() error {
	client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)

	svmresp, err := client.RebootVirtualMachine(d.ID)
	if err != nil {
		return err
	}

	return d.waitForJob(client, svmresp)
}

func (d *Driver) Kill() error {
	return d.Stop()
}

func (d *Driver) Remove() error {
	client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)

	// Destroy the SSH key
	if _, err := client.DeleteKeypair(d.KeyPair); err != nil {
		return err
	}

	// Destroy the virtual machine
	dvmresp, err := client.DestroyVirtualMachine(d.ID)
	if err != nil {
		return err
	}
	if err = d.waitForJob(client, dvmresp); err != nil {
		return err
	}
	return nil
}

func (d *Driver) jobIsDone(client *egoscale.Client, jobid string) (bool, error) {
	resp, err := client.PollAsyncJob(jobid)
	if err != nil {
		return true, err
	}
	switch resp.Jobstatus {
	case 0: // Job is still in progress
	case 1: // Job has successfully completed
		return true, nil
	case 2: // Job has failed to complete
		return true, fmt.Errorf("Operation failed to complete")
	default: // Some other code
	}
	return false, nil
}

func (d *Driver) waitForJob(client *egoscale.Client, jobid string) error {
	log.Infof("Waiting for job to complete...")
	return mcnutils.WaitForSpecificOrError(func() (bool, error) {
		return d.jobIsDone(client, jobid)
	}, 60, 2*time.Second)
}

func (d *Driver) waitForVM(client *egoscale.Client, jobid string) (*egoscale.DeployVirtualMachineResponse, error) {
	if err := d.waitForJob(client, jobid); err != nil {
		return nil, err
	}
	resp, err := client.PollAsyncJob(jobid)
	if err != nil {
		return nil, err
	}
	vm, err := client.AsyncToVirtualMachine(*resp)
	if err != nil {
		return nil, err
	}

	return vm, nil
}

// Build a cloud-init user data string that will install and run
// docker.
func (d *Driver) getCloudInit() (string, error) {
	if d.UserDataFile != "" {
		buf, err := ioutil.ReadFile(d.UserDataFile)
		if err != nil {
			return "", err
		}
		return string(buf), nil
	}

	return `#cloud-config
manage_etc_hosts: localhost
`, nil
}
<|endoftext|>


<commit_before>package zipkin

import (
	"math/rand"
	"net/http"
	"strconv"

	"golang.org/x/net/context"

	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/log"
)

// In Zipkin, "spans are considered to start and stop with the client." The
// client is responsible for creating a new span ID for each outgoing request,
// copying its span ID to the parent span ID, and maintaining the same trace
// ID. The server-receive and server-send annotations can be considered value
// added information and aren't strictly necessary.
//
// Further reading:
// • http://www.slideshare.net/johanoskarsson/zipkin-runtime-open-house
// • https://groups.google.com/forum/#!topic/zipkin-user/KilwtSA0g1k
// • https://gist.github.com/yoavaa/3478d3a0df666f21a98c

// Log is used to report diagnostic information. To enable it, swap in your
// application's logger.
var Log log.SwapLogger

const (
	// https://github.com/racker/tryfer#headers
	traceIDHTTPHeader      = "X-B3-TraceId"
	spanIDHTTPHeader       = "X-B3-SpanId"
	parentSpanIDHTTPHeader = "X-B3-ParentSpanId"

	// ClientSend is the annotation value used to mark a client sending a
	// request to a server.
	ClientSend = "cs"

	// ServerReceive is the annotation value used to mark a server's receipt
	// of a request from a client.
	ServerReceive = "sr"

	// ServerSend is the annotation value used to mark a server's completion
	// of a request and response to a client.
	ServerSend = "ss"

	// ClientReceive is the annotation value used to mark a client's receipt
	// of a completed request from a server.
	ClientReceive = "cr"
)

// AnnotateServer returns a server.Middleware that extracts a span from the
// context, adds server-receive and server-send annotations at the boundaries,
// and submits the span to the collector. If no span is found in the context,
// a new span is generated and inserted.
func AnnotateServer(newSpan NewSpanFunc, c Collector) endpoint.Middleware {
	return func(e endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (interface{}, error) {
			span, ok := fromContext(ctx)
			if !ok {
				span = newSpan(newID(), newID(), 0)
				ctx = context.WithValue(ctx, SpanContextKey, span)
			}
			span.Annotate(ServerReceive)
			defer func() { span.Annotate(ServerSend); c.Collect(span) }()
			return e(ctx, request)
		}
	}
}

// AnnotateClient returns a middleware that extracts a parent span from the
// context, produces a client (child) span from it, adds client-send and
// client-receive annotations at the boundaries, and submits the span to the
// collector. If no span is found in the context, a new span is generated and
// inserted.
func AnnotateClient(newSpan NewSpanFunc, c Collector) endpoint.Middleware {
	return func(e endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (interface{}, error) {
			var clientSpan *Span
			parentSpan, ok := fromContext(ctx)
			if ok {
				clientSpan = newSpan(parentSpan.TraceID(), newID(), parentSpan.SpanID())
			} else {
				clientSpan = newSpan(newID(), newID(), 0)
			}
			ctx = context.WithValue(ctx, SpanContextKey, clientSpan)                    // set
			defer func() { ctx = context.WithValue(ctx, SpanContextKey, parentSpan) }() // reset
			clientSpan.Annotate(ClientSend)
			defer func() { clientSpan.Annotate(ClientReceive); c.Collect(clientSpan) }()
			return e(ctx, request)
		}
	}
}

// ToContext returns a function that satisfies transport/http.BeforeFunc. It
// takes a Zipkin span from the incoming HTTP request, and saves it in the
// request context. It's designed to be wired into a server's HTTP transport
// Before stack.
func ToContext(newSpan NewSpanFunc) func(ctx context.Context, r *http.Request) context.Context {
	return func(ctx context.Context, r *http.Request) context.Context {
		return context.WithValue(ctx, SpanContextKey, fromHTTP(newSpan, r))
	}
}

// ToRequest returns a function that satisfies transport/http.BeforeFunc. It
// takes a Zipkin span from the context, and injects it into the HTTP request.
// It's designed to be wired into a client's HTTP transport Before stack. It's
// expected that AnnotateClient has already ensured the span in the context is
// a child/client span.
func ToRequest(newSpan NewSpanFunc) func(ctx context.Context, r *http.Request) context.Context {
	return func(ctx context.Context, r *http.Request) context.Context {
		span, ok := fromContext(ctx)
		if !ok {
			span = newSpan(newID(), newID(), 0)
		}
		if id := span.TraceID(); id > 0 {
			r.Header.Set(traceIDHTTPHeader, strconv.FormatInt(id, 16))
		}
		if id := span.SpanID(); id > 0 {
			r.Header.Set(spanIDHTTPHeader, strconv.FormatInt(id, 16))
		}
		if id := span.ParentSpanID(); id > 0 {
			r.Header.Set(parentSpanIDHTTPHeader, strconv.FormatInt(id, 16))
		}
		return ctx
	}
}

func fromHTTP(newSpan NewSpanFunc, r *http.Request) *Span {
	traceIDStr := r.Header.Get(traceIDHTTPHeader)
	if traceIDStr == "" {
		Log.Log("debug", "make new span")
		return newSpan(newID(), newID(), 0) // normal; just make a new one
	}
	traceID, err := strconv.ParseInt(traceIDStr, 16, 64)
	if err != nil {
		Log.Log(traceIDHTTPHeader, traceIDStr, "err", err)
		return newSpan(newID(), newID(), 0)
	}
	spanIDStr := r.Header.Get(spanIDHTTPHeader)
	if spanIDStr == "" {
		Log.Log("msg", "trace ID without span ID") // abnormal
		spanIDStr = strconv.FormatInt(newID(), 64) // deal with it
	}
	spanID, err := strconv.ParseInt(spanIDStr, 16, 64)
	if err != nil {
		Log.Log(spanIDHTTPHeader, spanIDStr, "err", err) // abnormal
		spanID = newID()                                 // deal with it
	}
	parentSpanIDStr := r.Header.Get(parentSpanIDHTTPHeader)
	if parentSpanIDStr == "" {
		parentSpanIDStr = "0" // normal
	}
	parentSpanID, err := strconv.ParseInt(parentSpanIDStr, 16, 64)
	if err != nil {
		Log.Log(parentSpanIDHTTPHeader, parentSpanIDStr, "err", err) // abnormal
		parentSpanID = 0                                             // the only way to deal with it
	}
	return newSpan(traceID, spanID, parentSpanID)
}

func fromContext(ctx context.Context) (*Span, bool) {
	val := ctx.Value(SpanContextKey)
	if val == nil {
		return nil, false
	}
	span, ok := val.(*Span)
	if !ok {
		panic(SpanContextKey + " value isn't a span object")
	}
	return span, true
}

func newID() int64 {
	// https://github.com/wadey/go-zipkin/blob/46e5f01/trace.go#L183-188
	// https://github.com/twitter/zipkin/issues/199
	// :(
	return rand.Int63() & 0x001fffffffffffff
}
<commit_msg>tracing/zipkin: use idiomatic next endpoint.Endpoint<commit_after>package zipkin

import (
	"math/rand"
	"net/http"
	"strconv"

	"golang.org/x/net/context"

	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/log"
)

// In Zipkin, "spans are considered to start and stop with the client." The
// client is responsible for creating a new span ID for each outgoing request,
// copying its span ID to the parent span ID, and maintaining the same trace
// ID. The server-receive and server-send annotations can be considered value
// added information and aren't strictly necessary.
//
// Further reading:
// • http://www.slideshare.net/johanoskarsson/zipkin-runtime-open-house
// • https://groups.google.com/forum/#!topic/zipkin-user/KilwtSA0g1k
// • https://gist.github.com/yoavaa/3478d3a0df666f21a98c

// Log is used to report diagnostic information. To enable it, swap in your
// application's logger.
var Log log.SwapLogger

const (
	// https://github.com/racker/tryfer#headers
	traceIDHTTPHeader      = "X-B3-TraceId"
	spanIDHTTPHeader       = "X-B3-SpanId"
	parentSpanIDHTTPHeader = "X-B3-ParentSpanId"

	// ClientSend is the annotation value used to mark a client sending a
	// request to a server.
	ClientSend = "cs"

	// ServerReceive is the annotation value used to mark a server's receipt
	// of a request from a client.
	ServerReceive = "sr"

	// ServerSend is the annotation value used to mark a server's completion
	// of a request and response to a client.
	ServerSend = "ss"

	// ClientReceive is the annotation value used to mark a client's receipt
	// of a completed request from a server.
	ClientReceive = "cr"
)

// AnnotateServer returns a server.Middleware that extracts a span from the
// context, adds server-receive and server-send annotations at the boundaries,
// and submits the span to the collector. If no span is found in the context,
// a new span is generated and inserted.
func AnnotateServer(newSpan NewSpanFunc, c Collector) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (interface{}, error) {
			span, ok := fromContext(ctx)
			if !ok {
				span = newSpan(newID(), newID(), 0)
				ctx = context.WithValue(ctx, SpanContextKey, span)
			}
			span.Annotate(ServerReceive)
			defer func() { span.Annotate(ServerSend); c.Collect(span) }()
			return next(ctx, request)
		}
	}
}

// AnnotateClient returns a middleware that extracts a parent span from the
// context, produces a client (child) span from it, adds client-send and
// client-receive annotations at the boundaries, and submits the span to the
// collector. If no span is found in the context, a new span is generated and
// inserted.
func AnnotateClient(newSpan NewSpanFunc, c Collector) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (interface{}, error) {
			var clientSpan *Span
			parentSpan, ok := fromContext(ctx)
			if ok {
				clientSpan = newSpan(parentSpan.TraceID(), newID(), parentSpan.SpanID())
			} else {
				clientSpan = newSpan(newID(), newID(), 0)
			}
			ctx = context.WithValue(ctx, SpanContextKey, clientSpan)                    // set
			defer func() { ctx = context.WithValue(ctx, SpanContextKey, parentSpan) }() // reset
			clientSpan.Annotate(ClientSend)
			defer func() { clientSpan.Annotate(ClientReceive); c.Collect(clientSpan) }()
			return next(ctx, request)
		}
	}
}

// ToContext returns a function that satisfies transport/http.BeforeFunc. It
// takes a Zipkin span from the incoming HTTP request, and saves it in the
// request context. It's designed to be wired into a server's HTTP transport
// Before stack.
func ToContext(newSpan NewSpanFunc) func(ctx context.Context, r *http.Request) context.Context {
	return func(ctx context.Context, r *http.Request) context.Context {
		return context.WithValue(ctx, SpanContextKey, fromHTTP(newSpan, r))
	}
}

// ToRequest returns a function that satisfies transport/http.BeforeFunc. It
// takes a Zipkin span from the context, and injects it into the HTTP request.
// It's designed to be wired into a client's HTTP transport Before stack. It's
// expected that AnnotateClient has already ensured the span in the context is
// a child/client span.
func ToRequest(newSpan NewSpanFunc) func(ctx context.Context, r *http.Request) context.Context {
	return func(ctx context.Context, r *http.Request) context.Context {
		span, ok := fromContext(ctx)
		if !ok {
			span = newSpan(newID(), newID(), 0)
		}
		if id := span.TraceID(); id > 0 {
			r.Header.Set(traceIDHTTPHeader, strconv.FormatInt(id, 16))
		}
		if id := span.SpanID(); id > 0 {
			r.Header.Set(spanIDHTTPHeader, strconv.FormatInt(id, 16))
		}
		if id := span.ParentSpanID(); id > 0 {
			r.Header.Set(parentSpanIDHTTPHeader, strconv.FormatInt(id, 16))
		}
		return ctx
	}
}

func fromHTTP(newSpan NewSpanFunc, r *http.Request) *Span {
	traceIDStr := r.Header.Get(traceIDHTTPHeader)
	if traceIDStr == "" {
		Log.Log("debug", "make new span")
		return newSpan(newID(), newID(), 0) // normal; just make a new one
	}
	traceID, err := strconv.ParseInt(traceIDStr, 16, 64)
	if err != nil {
		Log.Log(traceIDHTTPHeader, traceIDStr, "err", err)
		return newSpan(newID(), newID(), 0)
	}
	spanIDStr := r.Header.Get(spanIDHTTPHeader)
	if spanIDStr == "" {
		Log.Log("msg", "trace ID without span ID") // abnormal
		spanIDStr = strconv.FormatInt(newID(), 64) // deal with it
	}
	spanID, err := strconv.ParseInt(spanIDStr, 16, 64)
	if err != nil {
		Log.Log(spanIDHTTPHeader, spanIDStr, "err", err) // abnormal
		spanID = newID()                                 // deal with it
	}
	parentSpanIDStr := r.Header.Get(parentSpanIDHTTPHeader)
	if parentSpanIDStr == "" {
		parentSpanIDStr = "0" // normal
	}
	parentSpanID, err := strconv.ParseInt(parentSpanIDStr, 16, 64)
	if err != nil {
		Log.Log(parentSpanIDHTTPHeader, parentSpanIDStr, "err", err) // abnormal
		parentSpanID = 0                                             // the only way to deal with it
	}
	return newSpan(traceID, spanID, parentSpanID)
}

func fromContext(ctx context.Context) (*Span, bool) {
	val := ctx.Value(SpanContextKey)
	if val == nil {
		return nil, false
	}
	span, ok := val.(*Span)
	if !ok {
		panic(SpanContextKey + " value isn't a span object")
	}
	return span, true
}

func newID() int64 {
	// https://github.com/wadey/go-zipkin/blob/46e5f01/trace.go#L183-188
	// https://github.com/twitter/zipkin/issues/199
	// :(
	return rand.Int63() & 0x001fffffffffffff
}
<|endoftext|>


<commit_before>// Copyright 2015 The Chihaya Authors. All rights reserved.
// Use of this source code is governed by the BSD 2-Clause license,
// which can be found in the LICENSE file.

// Package models implements the common data types used throughout a BitTorrent
// tracker.
package models

import (
	"net"
	"strings"
	"time"

	"github.com/chihaya/chihaya/config"
)

var (
	// ErrMalformedRequest is returned when a request does not contain the
	// required parameters needed to create a model.
	ErrMalformedRequest = ClientError("malformed request")

	// ErrBadRequest is returned when a request is invalid in the peer's
	// current state. For example, announcing a "completed" event while
	// not a leecher or a "stopped" event while not active.
	ErrBadRequest = ClientError("bad request")

	// ErrUserDNE is returned when a user does not exist.
	ErrUserDNE = NotFoundError("user does not exist")

	// ErrTorrentDNE is returned when a torrent does not exist.
	ErrTorrentDNE = NotFoundError("torrent does not exist")

	// ErrClientUnapproved is returned when a clientID is not in the whitelist.
	ErrClientUnapproved = ClientError("client is not approved")

	// ErrInvalidPasskey is returned when a passkey is not properly formatted.
	ErrInvalidPasskey = ClientError("passkey is invalid")
)

type ClientError string
type NotFoundError ClientError

func (e ClientError) Error() string   { return string(e) }
func (e NotFoundError) Error() string { return string(e) }

type PeerList []Peer
type PeerKey string

func NewPeerKey(peerID string, ip net.IP) PeerKey {
	return PeerKey(peerID + "//" + ip.String())
}

func (pk PeerKey) IP() net.IP {
	ip := net.ParseIP(strings.Split(string(pk), "//")[1])
	if rval := ip.To4(); rval != nil {
		return rval
	}
	return ip
}

func (pk PeerKey) PeerID() string {
	return strings.Split(string(pk), "//")[0]
}

// Peer is a participant in a swarm.
type Peer struct {
	ID        string `json:"id"`
	UserID    uint64 `json:"user_id"`
	TorrentID uint64 `json:"torrent_id"`

	// Always has length net.IPv4len if IPv4, and net.IPv6len if IPv6
	IP net.IP `json:"ip,omitempty"`

	Port uint64 `json:"port"`

	Uploaded     uint64 `json:"uploaded"`
	Downloaded   uint64 `json:"downloaded"`
	Left         uint64 `json:"left"`
	LastAnnounce int64  `json:"last_announce"`
}

func (p *Peer) HasIPv4() bool {
	return !p.HasIPv6()
}

func (p *Peer) HasIPv6() bool {
	return len(p.IP) == net.IPv6len
}

func (p *Peer) Key() PeerKey {
	return NewPeerKey(p.ID, p.IP)
}

// Torrent is a swarm for a given torrent file.
type Torrent struct {
	ID       uint64 `json:"id"`
	Infohash string `json:"infohash"`

	Seeders  *PeerMap `json:"seeders"`
	Leechers *PeerMap `json:"leechers"`

	Snatches       uint64  `json:"snatches"`
	UpMultiplier   float64 `json:"up_multiplier"`
	DownMultiplier float64 `json:"down_multiplier"`
	LastAction     int64   `json:"last_action"`
}

// PeerCount returns the total number of peers connected on this Torrent.
func (t *Torrent) PeerCount() int {
	return t.Seeders.Len() + t.Leechers.Len()
}

// User is a registered user for private trackers.
type User struct {
	ID      uint64 `json:"id"`
	Passkey string `json:"passkey"`

	UpMultiplier   float64 `json:"up_multiplier"`
	DownMultiplier float64 `json:"down_multiplier"`
}

// Announce is an Announce by a Peer.
type Announce struct {
	Config *config.Config `json:"config"`

	Compact    bool   `json:"compact"`
	Downloaded uint64 `json:"downloaded"`
	Event      string `json:"event"`
	IPv4       net.IP `json:"ipv4"`
	IPv6       net.IP `json:"ipv6"`
	Infohash   string `json:"infohash"`
	Left       uint64 `json:"left"`
	NumWant    int    `json:"numwant"`
	Passkey    string `json:"passkey"`
	PeerID     string `json:"peer_id"`
	Port       uint64 `json:"port"`
	Uploaded   uint64 `json:"uploaded"`

	Torrent *Torrent `json:"-"`
	User    *User    `json:"-"`
	Peer    *Peer    `json:"-"`
	PeerV4  *Peer    `json:"-"` // Only valid if HasIPv4() is true.
	PeerV6  *Peer    `json:"-"` // Only valid if HasIPv6() is true.
}

// ClientID returns the part of a PeerID that identifies a Peer's client
// software.
func (a *Announce) ClientID() (clientID string) {
	length := len(a.PeerID)
	if length >= 6 {
		if a.PeerID[0] == '-' {
			if length >= 7 {
				clientID = a.PeerID[1:7]
			}
		} else {
			clientID = a.PeerID[0:6]
		}
	}

	return
}

func (a *Announce) HasIPv4() bool {
	return a.IPv4 != nil
}

func (a *Announce) HasIPv6() bool {
	return a.IPv6 != nil
}

// BuildPeer creates the Peer representation of an Announce. When provided nil
// for the user or torrent parameter, it creates a Peer{UserID: 0} or
// Peer{TorrentID: 0}, respectively. BuildPeer creates one peer for each IP
// in the announce, and panics if there are none.
func (a *Announce) BuildPeer(u *User, t *Torrent) {
	a.Peer = &Peer{
		ID:           a.PeerID,
		Port:         a.Port,
		Uploaded:     a.Uploaded,
		Downloaded:   a.Downloaded,
		Left:         a.Left,
		LastAnnounce: time.Now().Unix(),
	}

	if t != nil {
		a.Peer.TorrentID = t.ID
		a.Torrent = t
	}

	if u != nil {
		a.Peer.UserID = u.ID
		a.User = u
	}

	if a.HasIPv4() && a.HasIPv6() {
		a.PeerV4 = a.Peer
		a.PeerV4.IP = a.IPv4
		a.PeerV6 = &*a.Peer
		a.PeerV6.IP = a.IPv6
	} else if a.HasIPv4() {
		a.PeerV4 = a.Peer
		a.PeerV4.IP = a.IPv4
	} else if a.HasIPv6() {
		a.PeerV6 = a.Peer
		a.PeerV6.IP = a.IPv6
	} else {
		panic("models: announce must have an IP")
	}
	return
}

// AnnounceDelta contains the changes to a Peer's state. These changes are
// recorded by the backend driver.
type AnnounceDelta struct {
	Peer    *Peer
	Torrent *Torrent
	User    *User

	// Created is true if this announce created a new peer or changed an existing
	// peer's address
	Created bool
	// Snatched is true if this announce completed the download
	Snatched bool

	// Uploaded contains the upload delta for this announce, in bytes
	Uploaded    uint64
	RawUploaded uint64

	// Downloaded contains the download delta for this announce, in bytes
	Downloaded    uint64
	RawDownloaded uint64
}

// AnnounceResponse contains the information needed to fulfill an announce.
type AnnounceResponse struct {
	Complete, Incomplete  int
	Interval, MinInterval time.Duration
	IPv4Peers, IPv6Peers  PeerList

	Compact bool
}

// Scrape is a Scrape by a Peer.
type Scrape struct {
	Config *config.Config `json:"config"`

	Passkey    string
	Infohashes []string
}

// ScrapeResponse contains the information needed to fulfill a scrape.
type ScrapeResponse struct {
	Files []*Torrent
}
<commit_msg>models: include port in peerkey<commit_after>// Copyright 2015 The Chihaya Authors. All rights reserved.
// Use of this source code is governed by the BSD 2-Clause license,
// which can be found in the LICENSE file.

// Package models implements the common data types used throughout a BitTorrent
// tracker.
package models

import (
	"net"
	"strings"
	"time"

	"github.com/chihaya/chihaya/config"
)

var (
	// ErrMalformedRequest is returned when a request does not contain the
	// required parameters needed to create a model.
	ErrMalformedRequest = ClientError("malformed request")

	// ErrBadRequest is returned when a request is invalid in the peer's
	// current state. For example, announcing a "completed" event while
	// not a leecher or a "stopped" event while not active.
	ErrBadRequest = ClientError("bad request")

	// ErrUserDNE is returned when a user does not exist.
	ErrUserDNE = NotFoundError("user does not exist")

	// ErrTorrentDNE is returned when a torrent does not exist.
	ErrTorrentDNE = NotFoundError("torrent does not exist")

	// ErrClientUnapproved is returned when a clientID is not in the whitelist.
	ErrClientUnapproved = ClientError("client is not approved")

	// ErrInvalidPasskey is returned when a passkey is not properly formatted.
	ErrInvalidPasskey = ClientError("passkey is invalid")
)

type ClientError string
type NotFoundError ClientError

func (e ClientError) Error() string   { return string(e) }
func (e NotFoundError) Error() string { return string(e) }

type PeerList []Peer
type PeerKey string

func NewPeerKey(peerID string, ip net.IP, port string) PeerKey {
	return PeerKey(peerID + "//" + ip.String() + ":" + port)
}

func (pk PeerKey) IP() net.IP {
	ip := net.ParseIP(strings.Split(string(pk), "//")[1])
	if rval := ip.To4(); rval != nil {
		return rval
	}
	return ip
}

func (pk PeerKey) PeerID() string {
	return strings.Split(string(pk), "//")[0]
}

// Peer is a participant in a swarm.
type Peer struct {
	ID        string `json:"id"`
	UserID    uint64 `json:"user_id"`
	TorrentID uint64 `json:"torrent_id"`

	// Always has length net.IPv4len if IPv4, and net.IPv6len if IPv6
	IP net.IP `json:"ip,omitempty"`

	Port uint64 `json:"port"`

	Uploaded     uint64 `json:"uploaded"`
	Downloaded   uint64 `json:"downloaded"`
	Left         uint64 `json:"left"`
	LastAnnounce int64  `json:"last_announce"`
}

func (p *Peer) HasIPv4() bool {
	return !p.HasIPv6()
}

func (p *Peer) HasIPv6() bool {
	return len(p.IP) == net.IPv6len
}

func (p *Peer) Key() PeerKey {
	return NewPeerKey(p.ID, p.IP, string(p.Port))
}

// Torrent is a swarm for a given torrent file.
type Torrent struct {
	ID       uint64 `json:"id"`
	Infohash string `json:"infohash"`

	Seeders  *PeerMap `json:"seeders"`
	Leechers *PeerMap `json:"leechers"`

	Snatches       uint64  `json:"snatches"`
	UpMultiplier   float64 `json:"up_multiplier"`
	DownMultiplier float64 `json:"down_multiplier"`
	LastAction     int64   `json:"last_action"`
}

// PeerCount returns the total number of peers connected on this Torrent.
func (t *Torrent) PeerCount() int {
	return t.Seeders.Len() + t.Leechers.Len()
}

// User is a registered user for private trackers.
type User struct {
	ID      uint64 `json:"id"`
	Passkey string `json:"passkey"`

	UpMultiplier   float64 `json:"up_multiplier"`
	DownMultiplier float64 `json:"down_multiplier"`
}

// Announce is an Announce by a Peer.
type Announce struct {
	Config *config.Config `json:"config"`

	Compact    bool   `json:"compact"`
	Downloaded uint64 `json:"downloaded"`
	Event      string `json:"event"`
	IPv4       net.IP `json:"ipv4"`
	IPv6       net.IP `json:"ipv6"`
	Infohash   string `json:"infohash"`
	Left       uint64 `json:"left"`
	NumWant    int    `json:"numwant"`
	Passkey    string `json:"passkey"`
	PeerID     string `json:"peer_id"`
	Port       uint64 `json:"port"`
	Uploaded   uint64 `json:"uploaded"`

	Torrent *Torrent `json:"-"`
	User    *User    `json:"-"`
	Peer    *Peer    `json:"-"`
	PeerV4  *Peer    `json:"-"` // Only valid if HasIPv4() is true.
	PeerV6  *Peer    `json:"-"` // Only valid if HasIPv6() is true.
}

// ClientID returns the part of a PeerID that identifies a Peer's client
// software.
func (a *Announce) ClientID() (clientID string) {
	length := len(a.PeerID)
	if length >= 6 {
		if a.PeerID[0] == '-' {
			if length >= 7 {
				clientID = a.PeerID[1:7]
			}
		} else {
			clientID = a.PeerID[0:6]
		}
	}

	return
}

func (a *Announce) HasIPv4() bool {
	return a.IPv4 != nil
}

func (a *Announce) HasIPv6() bool {
	return a.IPv6 != nil
}

// BuildPeer creates the Peer representation of an Announce. When provided nil
// for the user or torrent parameter, it creates a Peer{UserID: 0} or
// Peer{TorrentID: 0}, respectively. BuildPeer creates one peer for each IP
// in the announce, and panics if there are none.
func (a *Announce) BuildPeer(u *User, t *Torrent) {
	a.Peer = &Peer{
		ID:           a.PeerID,
		Port:         a.Port,
		Uploaded:     a.Uploaded,
		Downloaded:   a.Downloaded,
		Left:         a.Left,
		LastAnnounce: time.Now().Unix(),
	}

	if t != nil {
		a.Peer.TorrentID = t.ID
		a.Torrent = t
	}

	if u != nil {
		a.Peer.UserID = u.ID
		a.User = u
	}

	if a.HasIPv4() && a.HasIPv6() {
		a.PeerV4 = a.Peer
		a.PeerV4.IP = a.IPv4
		a.PeerV6 = &*a.Peer
		a.PeerV6.IP = a.IPv6
	} else if a.HasIPv4() {
		a.PeerV4 = a.Peer
		a.PeerV4.IP = a.IPv4
	} else if a.HasIPv6() {
		a.PeerV6 = a.Peer
		a.PeerV6.IP = a.IPv6
	} else {
		panic("models: announce must have an IP")
	}
	return
}

// AnnounceDelta contains the changes to a Peer's state. These changes are
// recorded by the backend driver.
type AnnounceDelta struct {
	Peer    *Peer
	Torrent *Torrent
	User    *User

	// Created is true if this announce created a new peer or changed an existing
	// peer's address
	Created bool
	// Snatched is true if this announce completed the download
	Snatched bool

	// Uploaded contains the upload delta for this announce, in bytes
	Uploaded    uint64
	RawUploaded uint64

	// Downloaded contains the download delta for this announce, in bytes
	Downloaded    uint64
	RawDownloaded uint64
}

// AnnounceResponse contains the information needed to fulfill an announce.
type AnnounceResponse struct {
	Complete, Incomplete  int
	Interval, MinInterval time.Duration
	IPv4Peers, IPv6Peers  PeerList

	Compact bool
}

// Scrape is a Scrape by a Peer.
type Scrape struct {
	Config *config.Config `json:"config"`

	Passkey    string
	Infohashes []string
}

// ScrapeResponse contains the information needed to fulfill a scrape.
type ScrapeResponse struct {
	Files []*Torrent
}
<|endoftext|>