<commit_before>package vm

import "github.com/joushou/gocnc/gcode"
import "math"
import "fmt"
import "errors"

//
// The CNC interpreter/"vm"
//
// It currently supports:
//
//   G00   - rapid move
//   G01   - linear move
//   G02   - cw arc
//   G03   - ccw arc
//   G17   - xy arc plane
//   G18   - xz arc plane
//   G19   - yz arc plane
//   G20   - imperial mode
//   G21   - metric mode
//   G80   - cancel mode (?)
//   G90   - absolute
//   G90.1 - absolute arc
//   G91   - relative
//   G91.1 - relative arc
//
//   M02 - end of program
//   M03 - spindle enable clockwise
//   M04 - spindle enable counterclockwise
//   M05 - spindle disable
//   M07 - mist coolant enable
//   M08 - flood coolant enable
//   M09 - coolant disable
//   M30 - end of program
//
//   F - feedrate
//   S - spindle speed
//   P - parameter
//   X, Y, Z - cartesian movement
//   I, J, K - arc center definition

type Statement map[rune]float64

//
// State structs
//

const (
	moveModeNone   = iota
	moveModeRapid  = iota
	moveModeLinear = iota
	moveModeCWArc  = iota
	moveModeCCWArc = iota
)

const (
	planeXY = iota
	planeXZ = iota
	planeYZ = iota
)

const (
	vmModeNone        = iota
	vmModePositioning = iota
)

type State struct {
	feedrate         float64
	spindleSpeed     float64
	moveMode         int
	spindleEnabled   bool
	spindleClockwise bool
	floodCoolant     bool
	mistCoolant      bool
}

type Position struct {
	state   State
	x, y, z float64
}

type Machine struct {
	state        State
	metric       bool
	absoluteMove bool
	absoluteArc  bool
	movePlane    int
	completed    bool
	posStack     []Position
}

//
// Positioning
//

// Retrieves position from top of stack
func (vm *Machine) curPos() Position {
	return vm.posStack[len(vm.posStack)-1]
}

// Appends a position to the stack
func (vm *Machine) addPos(pos Position) {
	vm.posStack = append(vm.posStack, pos)
}

// Calculates the absolute position of the given statement, including optional I, J, K parameters
func (vm *Machine) calcPos(stmt Statement) (newX, newY, newZ, newI, newJ, newK float64) {
	pos := vm.curPos()
	var ok bool

	if newX, ok = stmt['X']; !ok {
		newX = pos.x
	} else if !vm.metric {
		newX *= 25.4
	}

	if newY, ok = stmt['Y']; !ok {
		newY = pos.y
	} else if !vm.metric {
		newY *= 25.4
	}

	if newZ, ok = stmt['Z']; !ok {
		newZ = pos.z
	} else if !vm.metric {
		newZ *= 25.4
	}

	newI = stmt['I']
	newJ = stmt['J']
	newK = stmt['K']

	if !vm.metric {
		newI, newJ, newK = newI*25.4, newJ*25.4, newZ*25.4
	}

	if !vm.absoluteMove {
		newX, newY, newZ = pos.x+newX, pos.y+newY, pos.z+newZ
	}

	if !vm.absoluteArc {
		newI, newJ, newK = pos.x+newI, pos.y+newJ, pos.z+newK
	}
	return newX, newY, newZ, newI, newJ, newK
}

func (vm *Machine) positioning(stmt Statement) {
	newX, newY, newZ, _, _, _ := vm.calcPos(stmt)
	vm.addPos(Position{vm.state, newX, newY, newZ})
}

// Calculates an approximate arc from the provided statement
func (vm *Machine) approximateArc(stmt Statement, pointDistance float64, ignoreRadiusErrors bool) {
	startPos := vm.curPos()
	startX, startY, startZ := startPos.x, startPos.y, startPos.z
	endX, endY, endZ, endI, endJ, endK := vm.calcPos(stmt)

	P := 0.0
	if stmt['P'] > 1 {
		P = stmt['P'] - 1
	}

	clockwise := (vm.state.moveMode == moveModeCWArc)

	vm.state.moveMode = moveModeLinear

	var add func(x, y, z float64)

	switch vm.movePlane {
	case planeXY:
		add = func(x, y, z float64) {
			vm.positioning(Statement{'X': x, 'Y': y, 'Z': z})
		}
	case planeXZ:
		startY, startZ = startZ, startY
		endY, endZ = endZ, endY
		endJ, endK = endK, endJ
		add = func(x, y, z float64) {
			vm.positioning(Statement{'X': x, 'Y': z, 'Z': y})
		}
	case planeYZ:
		startX, startZ = startZ, startX
		endX, endZ = endZ, endX
		endI, endK = endK, endI
		add = func(x, y, z float64) {
			vm.positioning(Statement{'X': z, 'Y': y, 'Z': x})
		}
	}

	radius1 := math.Sqrt(math.Pow(endI-startX, 2) + math.Pow(endJ-startY, 2))
	radius2 := math.Sqrt(math.Pow(endI-endX, 2) + math.Pow(endJ-endX, 2))

	if math.Abs((radius2-radius1)/radius1) > 0.01 && !ignoreRadiusErrors {
		panic(fmt.Sprintf("Radius deviation of %f percent", math.Abs((radius2-radius1)/radius1)*100))
	}

	theta1 := math.Atan2((startY - endJ), (startX - endI))
	theta2 := math.Atan2((endY - endJ), (endX - endI))

	angleDiff := theta2 - theta1
	if angleDiff < 0 && !clockwise {
		angleDiff += 2*math.Pi
	} else if angleDiff > 0 && clockwise {
		angleDiff -= 2*math.Pi
	}

	if clockwise {
		angleDiff -= P*2*math.Pi
	} else {
		angleDiff += P*2*math.Pi
	}

	arcLen := math.Abs(angleDiff) * math.Sqrt(math.Pow(radius1, 2)+math.Pow((endZ-startZ)/angleDiff, 2))
	steps := int(arcLen / pointDistance)

	angle := 0.0
	for i := 0; i <= steps; i++ {
		if clockwise {
			angle = theta1 + angleDiff/float64(steps)*float64(i)
		} else {
			angle = theta1 + angleDiff/float64(steps)*float64(i)
		}
		x, y := endI+radius1*math.Cos(angle), endJ+radius1*math.Sin(angle)
		z := startZ + (endZ-startZ)/float64(steps)*float64(i)
		add(x, y, z)
	}
	add(endX,endY,endZ)
}

//
// Dispatch
//
func (vm *Machine) run(stmt Statement) (err error) {
	if vm.completed {
		// A stop had previously been issued
		return
	}

	defer func() {
		if r := recover(); r != nil {
			err = errors.New(fmt.Sprintf("%s", r))
		}
	}()

	// G-codes
	if g, ok := stmt['G']; ok {
		switch g {
		case 0:
			vm.state.moveMode = moveModeRapid
		case 1:
			vm.state.moveMode = moveModeLinear
		case 2:
			vm.state.moveMode = moveModeCWArc
		case 3:
			vm.state.moveMode = moveModeCCWArc
		case 17:
			vm.movePlane = planeXY
		case 18:
			vm.movePlane = planeXZ
		case 19:
			vm.movePlane = planeYZ
		case 20:
			vm.metric = false
		case 21:
			vm.metric = true
		case 80:
			vm.state.moveMode = moveModeNone
		case 90:
			vm.absoluteMove = true
		case 90.1:
			vm.absoluteArc = true
		case 91:
			vm.absoluteMove = false
		case 91.1:
			vm.absoluteArc = false
		}
	}

	// M-codes
	if g, ok := stmt['M']; ok {
		switch g {
		case 2:
			vm.completed = true
		case 3:
			vm.state.spindleEnabled = true
			vm.state.spindleClockwise = true
		case 4:
			vm.state.spindleEnabled = true
			vm.state.spindleClockwise = false
		case 5:
			vm.state.spindleEnabled = false
		case 7:
			vm.state.mistCoolant = true
		case 8:
			vm.state.floodCoolant = true
		case 9:
			vm.state.mistCoolant = false
			vm.state.floodCoolant = false
		case 30:
			vm.completed = true
		}
	}

	// F-codes
	if g, ok := stmt['F']; ok {
		if !vm.metric {
			g *= 25.4
		}
		if g <= 0 {
			return errors.New("Feedrate must be greater than zero")
		}
		vm.state.feedrate = g
	}

	// S-codes
	if g, ok := stmt['S']; ok {
		if g < 0 {
			return errors.New("Spindle speed must be greater than or equal to zero")
		}
		vm.state.spindleSpeed = g
	}

	// X, Y, Z, I, J, K, P
	_, hasX := stmt['X']
	_, hasY := stmt['Y']
	_, hasZ := stmt['Z']
	if hasX || hasY || hasZ {
		if vm.state.moveMode == moveModeCWArc || vm.state.moveMode == moveModeCCWArc {
			vm.approximateArc(stmt, 0.1, false)
		} else if vm.state.moveMode == moveModeLinear || vm.state.moveMode == moveModeRapid {
			vm.positioning(stmt)
		} else {
			return errors.New("Move attempted without an active move mode")
		}
	}

	return nil
}

// Ensure that machine state is correct after execution
func (vm *Machine) finalize() {
	if vm.state != vm.curPos().state {
		vm.state.moveMode = moveModeNone
		vm.addPos(Position{state: vm.state})
	}
}

//
// Initialize VM state
//
func (vm *Machine) Init() {
	vm.posStack = append(vm.posStack, Position{})
	vm.metric = true
	vm.absoluteMove = true
	vm.absoluteArc = true
	vm.movePlane = planeXY
}

//
// Process an AST
//
func (vm *Machine) Process(doc *gcode.Document) (err error) {
	for _, b := range doc.Blocks {
		if b.BlockDelete {
			continue
		}

		stmt := make(Statement)
		for _, n := range b.Nodes {
			if word, ok := n.(*gcode.Word); ok {
				stmt[word.Address] = word.Command
			}
		}
		if err := vm.run(stmt); err != nil {
			return err
		}
	}
	vm.finalize()
	return
}
<commit_msg>Remove redundant conditioN<commit_after>package vm

import "github.com/joushou/gocnc/gcode"
import "math"
import "fmt"
import "errors"

//
// The CNC interpreter/"vm"
//
// It currently supports:
//
//   G00   - rapid move
//   G01   - linear move
//   G02   - cw arc
//   G03   - ccw arc
//   G17   - xy arc plane
//   G18   - xz arc plane
//   G19   - yz arc plane
//   G20   - imperial mode
//   G21   - metric mode
//   G80   - cancel mode (?)
//   G90   - absolute
//   G90.1 - absolute arc
//   G91   - relative
//   G91.1 - relative arc
//
//   M02 - end of program
//   M03 - spindle enable clockwise
//   M04 - spindle enable counterclockwise
//   M05 - spindle disable
//   M07 - mist coolant enable
//   M08 - flood coolant enable
//   M09 - coolant disable
//   M30 - end of program
//
//   F - feedrate
//   S - spindle speed
//   P - parameter
//   X, Y, Z - cartesian movement
//   I, J, K - arc center definition

type Statement map[rune]float64

//
// State structs
//

const (
	moveModeNone   = iota
	moveModeRapid  = iota
	moveModeLinear = iota
	moveModeCWArc  = iota
	moveModeCCWArc = iota
)

const (
	planeXY = iota
	planeXZ = iota
	planeYZ = iota
)

const (
	vmModeNone        = iota
	vmModePositioning = iota
)

type State struct {
	feedrate         float64
	spindleSpeed     float64
	moveMode         int
	spindleEnabled   bool
	spindleClockwise bool
	floodCoolant     bool
	mistCoolant      bool
}

type Position struct {
	state   State
	x, y, z float64
}

type Machine struct {
	state        State
	metric       bool
	absoluteMove bool
	absoluteArc  bool
	movePlane    int
	completed    bool
	posStack     []Position
}

//
// Positioning
//

// Retrieves position from top of stack
func (vm *Machine) curPos() Position {
	return vm.posStack[len(vm.posStack)-1]
}

// Appends a position to the stack
func (vm *Machine) addPos(pos Position) {
	vm.posStack = append(vm.posStack, pos)
}

// Calculates the absolute position of the given statement, including optional I, J, K parameters
func (vm *Machine) calcPos(stmt Statement) (newX, newY, newZ, newI, newJ, newK float64) {
	pos := vm.curPos()
	var ok bool

	if newX, ok = stmt['X']; !ok {
		newX = pos.x
	} else if !vm.metric {
		newX *= 25.4
	}

	if newY, ok = stmt['Y']; !ok {
		newY = pos.y
	} else if !vm.metric {
		newY *= 25.4
	}

	if newZ, ok = stmt['Z']; !ok {
		newZ = pos.z
	} else if !vm.metric {
		newZ *= 25.4
	}

	newI = stmt['I']
	newJ = stmt['J']
	newK = stmt['K']

	if !vm.metric {
		newI, newJ, newK = newI*25.4, newJ*25.4, newZ*25.4
	}

	if !vm.absoluteMove {
		newX, newY, newZ = pos.x+newX, pos.y+newY, pos.z+newZ
	}

	if !vm.absoluteArc {
		newI, newJ, newK = pos.x+newI, pos.y+newJ, pos.z+newK
	}
	return newX, newY, newZ, newI, newJ, newK
}

func (vm *Machine) positioning(stmt Statement) {
	newX, newY, newZ, _, _, _ := vm.calcPos(stmt)
	vm.addPos(Position{vm.state, newX, newY, newZ})
}

// Calculates an approximate arc from the provided statement
func (vm *Machine) approximateArc(stmt Statement, pointDistance float64, ignoreRadiusErrors bool) {
	startPos := vm.curPos()
	startX, startY, startZ := startPos.x, startPos.y, startPos.z
	endX, endY, endZ, endI, endJ, endK := vm.calcPos(stmt)

	P := 0.0
	if stmt['P'] > 1 {
		P = stmt['P'] - 1
	}

	clockwise := (vm.state.moveMode == moveModeCWArc)

	vm.state.moveMode = moveModeLinear

	var add func(x, y, z float64)

	switch vm.movePlane {
	case planeXY:
		add = func(x, y, z float64) {
			vm.positioning(Statement{'X': x, 'Y': y, 'Z': z})
		}
	case planeXZ:
		startY, startZ = startZ, startY
		endY, endZ = endZ, endY
		endJ, endK = endK, endJ
		add = func(x, y, z float64) {
			vm.positioning(Statement{'X': x, 'Y': z, 'Z': y})
		}
	case planeYZ:
		startX, startZ = startZ, startX
		endX, endZ = endZ, endX
		endI, endK = endK, endI
		add = func(x, y, z float64) {
			vm.positioning(Statement{'X': z, 'Y': y, 'Z': x})
		}
	}

	radius1 := math.Sqrt(math.Pow(endI-startX, 2) + math.Pow(endJ-startY, 2))
	radius2 := math.Sqrt(math.Pow(endI-endX, 2) + math.Pow(endJ-endX, 2))

	if math.Abs((radius2-radius1)/radius1) > 0.01 && !ignoreRadiusErrors {
		panic(fmt.Sprintf("Radius deviation of %f percent", math.Abs((radius2-radius1)/radius1)*100))
	}

	theta1 := math.Atan2((startY - endJ), (startX - endI))
	theta2 := math.Atan2((endY - endJ), (endX - endI))

	angleDiff := theta2 - theta1
	if angleDiff < 0 && !clockwise {
		angleDiff += 2 * math.Pi
	} else if angleDiff > 0 && clockwise {
		angleDiff -= 2 * math.Pi
	}

	if clockwise {
		angleDiff -= P * 2 * math.Pi
	} else {
		angleDiff += P * 2 * math.Pi
	}

	arcLen := math.Abs(angleDiff) * math.Sqrt(math.Pow(radius1, 2)+math.Pow((endZ-startZ)/angleDiff, 2))
	steps := int(arcLen / pointDistance)

	angle := 0.0
	for i := 0; i <= steps; i++ {
		angle = theta1 + angleDiff/float64(steps)*float64(i)
		x, y := endI+radius1*math.Cos(angle), endJ+radius1*math.Sin(angle)
		z := startZ + (endZ-startZ)/float64(steps)*float64(i)
		add(x, y, z)
	}
	add(endX, endY, endZ)
}

//
// Dispatch
//
func (vm *Machine) run(stmt Statement) (err error) {
	if vm.completed {
		// A stop had previously been issued
		return
	}

	defer func() {
		if r := recover(); r != nil {
			err = errors.New(fmt.Sprintf("%s", r))
		}
	}()

	// G-codes
	if g, ok := stmt['G']; ok {
		switch g {
		case 0:
			vm.state.moveMode = moveModeRapid
		case 1:
			vm.state.moveMode = moveModeLinear
		case 2:
			vm.state.moveMode = moveModeCWArc
		case 3:
			vm.state.moveMode = moveModeCCWArc
		case 17:
			vm.movePlane = planeXY
		case 18:
			vm.movePlane = planeXZ
		case 19:
			vm.movePlane = planeYZ
		case 20:
			vm.metric = false
		case 21:
			vm.metric = true
		case 80:
			vm.state.moveMode = moveModeNone
		case 90:
			vm.absoluteMove = true
		case 90.1:
			vm.absoluteArc = true
		case 91:
			vm.absoluteMove = false
		case 91.1:
			vm.absoluteArc = false
		}
	}

	// M-codes
	if g, ok := stmt['M']; ok {
		switch g {
		case 2:
			vm.completed = true
		case 3:
			vm.state.spindleEnabled = true
			vm.state.spindleClockwise = true
		case 4:
			vm.state.spindleEnabled = true
			vm.state.spindleClockwise = false
		case 5:
			vm.state.spindleEnabled = false
		case 7:
			vm.state.mistCoolant = true
		case 8:
			vm.state.floodCoolant = true
		case 9:
			vm.state.mistCoolant = false
			vm.state.floodCoolant = false
		case 30:
			vm.completed = true
		}
	}

	// F-codes
	if g, ok := stmt['F']; ok {
		if !vm.metric {
			g *= 25.4
		}
		if g <= 0 {
			return errors.New("Feedrate must be greater than zero")
		}
		vm.state.feedrate = g
	}

	// S-codes
	if g, ok := stmt['S']; ok {
		if g < 0 {
			return errors.New("Spindle speed must be greater than or equal to zero")
		}
		vm.state.spindleSpeed = g
	}

	// X, Y, Z, I, J, K, P
	_, hasX := stmt['X']
	_, hasY := stmt['Y']
	_, hasZ := stmt['Z']
	if hasX || hasY || hasZ {
		if vm.state.moveMode == moveModeCWArc || vm.state.moveMode == moveModeCCWArc {
			vm.approximateArc(stmt, 0.1, false)
		} else if vm.state.moveMode == moveModeLinear || vm.state.moveMode == moveModeRapid {
			vm.positioning(stmt)
		} else {
			return errors.New("Move attempted without an active move mode")
		}
	}

	return nil
}

// Ensure that machine state is correct after execution
func (vm *Machine) finalize() {
	if vm.state != vm.curPos().state {
		vm.state.moveMode = moveModeNone
		vm.addPos(Position{state: vm.state})
	}
}

//
// Initialize VM state
//
func (vm *Machine) Init() {
	vm.posStack = append(vm.posStack, Position{})
	vm.metric = true
	vm.absoluteMove = true
	vm.absoluteArc = true
	vm.movePlane = planeXY
}

//
// Process an AST
//
func (vm *Machine) Process(doc *gcode.Document) (err error) {
	for _, b := range doc.Blocks {
		if b.BlockDelete {
			continue
		}

		stmt := make(Statement)
		for _, n := range b.Nodes {
			if word, ok := n.(*gcode.Word); ok {
				stmt[word.Address] = word.Command
			}
		}
		if err := vm.run(stmt); err != nil {
			return err
		}
	}
	vm.finalize()
	return
}
<|endoftext|>


<commit_before>package jupiterbrain

import (
	"fmt"
	"net/url"
	"sync"

	"code.google.com/p/go-uuid/uuid"

	"github.com/Sirupsen/logrus"
	"github.com/vmware/govmomi"
	"github.com/vmware/govmomi/object"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/types"
	"golang.org/x/net/context"
)

type vSphereInstanceManager struct {
	log *logrus.Logger

	vSphereClientMutex sync.Mutex
	vSphereClient      *govmomi.Client

	vSphereURL *url.URL

	paths VSpherePaths
}

// VSpherePaths holds some vSphere inventory paths that are required for the
// InstanceManager to function properly.
type VSpherePaths struct {
	// BasePath is the path to a folder in which the base VMs that the
	// instance manager use to create VMs are. These VMs should have a
	// snapshot named "base", and new VMs will spin up from this snapshot.
	// The path should end with a slash.
	BasePath string

	// VMPath is the path to a folder in which VMs will be cloned into. Any
	// VM in this folder can be displayed and stopped with the instance
	// manager. The path should end with a slash.
	VMPath string

	// ClusterPath is the inventory path to a ClusterComputeResource that
	// is used as the resource pool for new VMs.
	ClusterPath string
}

// NewVSphereInstanceManager creates a new instance manager backed by vSphere
func NewVSphereInstanceManager(log *logrus.Logger, vSphereURL *url.URL, paths VSpherePaths) InstanceManager {
	return &vSphereInstanceManager{
		log:        log,
		vSphereURL: vSphereURL,
		paths:      paths,
	}
}

func (i *vSphereInstanceManager) Fetch(ctx context.Context, id string) (*Instance, error) {
	client, err := i.client(ctx)
	if err != nil {
		return nil, err
	}

	searchIndex := object.NewSearchIndex(client.Client)

	vmRef, err := searchIndex.FindByInventoryPath(ctx, i.paths.VMPath+id)
	if err != nil {
		return nil, VSphereAPIError{UnderlyingError: err}
	}

	if vmRef == nil {
		return nil, VirtualMachineNotFoundError{Path: i.paths.VMPath, ID: id}
	}

	vm, ok := vmRef.(*object.VirtualMachine)
	if !ok {
		return nil, fmt.Errorf("object at path %s is a %T, but expected VirtualMachine", i.paths.VMPath+id, vmRef)
	}

	return i.instanceForVirtualMachine(ctx, vm)
}

func (i *vSphereInstanceManager) List(ctx context.Context) ([]*Instance, error) {
	folder, err := i.vmFolder(ctx)
	if err != nil {
		return nil, err
	}

	vms, err := folder.Children(ctx)
	if err != nil {
		return nil, VSphereAPIError{UnderlyingError: err}
	}

	var instances []*Instance
	for _, vmRef := range vms {
		vm, ok := vmRef.(*object.VirtualMachine)
		if !ok {
			continue
		}

		instance, err := i.instanceForVirtualMachine(ctx, vm)
		if err != nil {
			return nil, err
		}

		instances = append(instances, instance)
	}

	return instances, nil
}

func (i *vSphereInstanceManager) Start(ctx context.Context, base string) (*Instance, error) {
	client, err := i.client(ctx)
	if err != nil {
		return nil, err
	}

	searchIndex := object.NewSearchIndex(client.Client)

	vmRef, err := searchIndex.FindByInventoryPath(ctx, i.paths.BasePath+base)
	if err != nil {
		return nil, err
	}

	if vmRef == nil {
		return nil, BaseVirtualMachineNotFoundError{Path: i.paths.BasePath, Name: base}
	}

	vm, ok := vmRef.(*object.VirtualMachine)
	if !ok {
		return nil, fmt.Errorf("base VM %s is a %T, but expected VirtualMachine", base, vmRef)
	}

	var mvm mo.VirtualMachine
	err = vm.Properties(ctx, vm.Reference(), []string{"snapshot"}, &mvm)
	if err != nil {
		return nil, fmt.Errorf("couldn't get snapshot info for base VM: %s", err)
	}

	snapshotTree, ok := i.findSnapshot(mvm.Snapshot.RootSnapshotList, "base")
	if !ok {
		return nil, fmt.Errorf("invalid base VM (no snapshot)")
	}

	resourcePool, err := i.resourcePool(ctx)
	if err != nil {
		return nil, fmt.Errorf("couldn't get resource pool: %s", err)
	}

	relocateSpec := types.VirtualMachineRelocateSpec{
		DiskMoveType: string(types.VirtualMachineRelocateDiskMoveOptionsCreateNewChildDiskBacking),
		Pool:         resourcePool,
	}

	cloneSpec := types.VirtualMachineCloneSpec{
		Location: relocateSpec,
		PowerOn:  true,
		Template: false,
		Snapshot: &snapshotTree.Snapshot,
	}

	name := uuid.New()

	vmFolder, err := i.vmFolder(ctx)
	if err != nil {
		return nil, err
	}

	task, err := vm.Clone(ctx, vmFolder, name, cloneSpec)
	if err != nil {
		return nil, err
	}

	err = task.Wait(ctx)
	if err != nil {
		return nil, err
	}

	var mt mo.Task
	err = task.Properties(ctx, task.Reference(), []string{"info"}, &mt)
	if err != nil {
		return nil, err
	}

	if mt.Info.Result == nil {
		return nil, fmt.Errorf("expected VM, but got nil")
	}

	vmManagedRef, ok := mt.Info.Result.(types.ManagedObjectReference)
	if !ok {
		return nil, fmt.Errorf("expected ManagedObjectReference, but got %T", mt.Info.Result)
	}

	return i.instanceForVirtualMachine(ctx, object.NewVirtualMachine(client.Client, vmManagedRef))
}

func (i *vSphereInstanceManager) Terminate(ctx context.Context, id string) error {
	client, err := i.client(ctx)
	if err != nil {
		return err
	}

	searchIndex := object.NewSearchIndex(client.Client)

	vmRef, err := searchIndex.FindByInventoryPath(ctx, i.paths.VMPath+id)
	if err != nil {
		return err
	}

	if vmRef == nil {
		return VirtualMachineNotFoundError{Path: i.paths.VMPath, ID: id}
	}

	vm, ok := vmRef.(*object.VirtualMachine)
	if !ok {
		return fmt.Errorf("not a VM")
	}

	task, err := vm.PowerOff(ctx)
	if err != nil {
		return err
	}

	err = task.Wait(ctx)
	if err != nil {
		return err
	}

	task, err = vm.Destroy(ctx)
	if err != nil {
		return err
	}

	return task.Wait(ctx)
}

func (i *vSphereInstanceManager) client(ctx context.Context) (*govmomi.Client, error) {
	i.vSphereClientMutex.Lock()
	defer i.vSphereClientMutex.Unlock()

	if i.vSphereClient == nil {
		client, err := govmomi.NewClient(ctx, i.vSphereURL, true)
		if err != nil {
			return nil, fmt.Errorf("couldn't create vSphere client: %s", err)
		}

		i.vSphereClient = client
		return i.vSphereClient, nil
	}

	active, err := i.vSphereClient.SessionManager.SessionIsActive(ctx)
	if err != nil {
		client, err := govmomi.NewClient(ctx, i.vSphereURL, true)
		if err != nil {
			return nil, fmt.Errorf("couldn't create vSphere client: %s", err)
		}

		i.vSphereClient = client
		return i.vSphereClient, nil
	}

	if !active {
		err := i.vSphereClient.SessionManager.Login(ctx, i.vSphereURL.User)
		if err != nil {
			return nil, fmt.Errorf("couldn't log in to vSphere API: %s", err)
		}
	}

	return i.vSphereClient, nil
}

func (i *vSphereInstanceManager) vmFolder(ctx context.Context) (*object.Folder, error) {
	client, err := i.client(ctx)
	if err != nil {
		return nil, err
	}

	searchIndex := object.NewSearchIndex(client.Client)

	folderRef, err := searchIndex.FindByInventoryPath(ctx, i.paths.VMPath)
	if err != nil {
		return nil, err
	}

	if folderRef == nil {
		return nil, fmt.Errorf("VM folder not found")
	}

	folder, ok := folderRef.(*object.Folder)
	if !ok {
		return nil, fmt.Errorf("VM folder is not a folder but %T", folderRef)
	}

	return folder, nil
}

func (i *vSphereInstanceManager) resourcePool(ctx context.Context) (*types.ManagedObjectReference, error) {
	client, err := i.client(ctx)
	if err != nil {
		return nil, err
	}

	searchIndex := object.NewSearchIndex(client.Client)

	clusterRef, err := searchIndex.FindByInventoryPath(ctx, i.paths.ClusterPath)
	if err != nil {
		return nil, err
	}

	if clusterRef == nil {
		return nil, fmt.Errorf("cluster not found at %s", i.paths.ClusterPath)
	}

	cluster, ok := clusterRef.(*object.ClusterComputeResource)
	if !ok {
		return nil, fmt.Errorf("object at %s is %T, but expected ComputeClusterResource", i.paths.ClusterPath, clusterRef)
	}

	var mccr mo.ClusterComputeResource
	err = cluster.Properties(ctx, cluster.Reference(), []string{"resourcePool"}, &mccr)
	if err != nil {
		return nil, err
	}

	return mccr.ResourcePool, nil
}

func (i *vSphereInstanceManager) findSnapshot(roots []types.VirtualMachineSnapshotTree, name string) (types.VirtualMachineSnapshotTree, bool) {
	for _, snapshotTree := range roots {
		if snapshotTree.Name == name {
			return snapshotTree, true
		}

		tree, ok := i.findSnapshot(snapshotTree.ChildSnapshotList, name)
		if ok {
			return tree, true
		}
	}

	return types.VirtualMachineSnapshotTree{}, false
}

func (i *vSphereInstanceManager) instanceForVirtualMachine(ctx context.Context, vm *object.VirtualMachine) (*Instance, error) {
	var mvm mo.VirtualMachine
	err := vm.Properties(ctx, vm.Reference(), []string{"config", "guest", "runtime"}, &mvm)
	if err != nil {
		return nil, err
	}

	var ipAddresses []string
	for _, nic := range mvm.Guest.Net {
		for _, ip := range nic.IpConfig.IpAddress {
			ipAddresses = append(ipAddresses, ip.IpAddress)
		}
	}

	return &Instance{
		ID:          mvm.Config.Name,
		IPAddresses: ipAddresses,
		State:       string(mvm.Runtime.PowerState),
	}, nil
}
<commit_msg>Error out from missing snapshot prior to reading through nil pointer<commit_after>package jupiterbrain

import (
	"fmt"
	"net/url"
	"sync"

	"code.google.com/p/go-uuid/uuid"

	"github.com/Sirupsen/logrus"
	"github.com/vmware/govmomi"
	"github.com/vmware/govmomi/object"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/types"
	"golang.org/x/net/context"
)

type vSphereInstanceManager struct {
	log *logrus.Logger

	vSphereClientMutex sync.Mutex
	vSphereClient      *govmomi.Client

	vSphereURL *url.URL

	paths VSpherePaths
}

// VSpherePaths holds some vSphere inventory paths that are required for the
// InstanceManager to function properly.
type VSpherePaths struct {
	// BasePath is the path to a folder in which the base VMs that the
	// instance manager use to create VMs are. These VMs should have a
	// snapshot named "base", and new VMs will spin up from this snapshot.
	// The path should end with a slash.
	BasePath string

	// VMPath is the path to a folder in which VMs will be cloned into. Any
	// VM in this folder can be displayed and stopped with the instance
	// manager. The path should end with a slash.
	VMPath string

	// ClusterPath is the inventory path to a ClusterComputeResource that
	// is used as the resource pool for new VMs.
	ClusterPath string
}

// NewVSphereInstanceManager creates a new instance manager backed by vSphere
func NewVSphereInstanceManager(log *logrus.Logger, vSphereURL *url.URL, paths VSpherePaths) InstanceManager {
	return &vSphereInstanceManager{
		log:        log,
		vSphereURL: vSphereURL,
		paths:      paths,
	}
}

func (i *vSphereInstanceManager) Fetch(ctx context.Context, id string) (*Instance, error) {
	client, err := i.client(ctx)
	if err != nil {
		return nil, err
	}

	searchIndex := object.NewSearchIndex(client.Client)

	vmRef, err := searchIndex.FindByInventoryPath(ctx, i.paths.VMPath+id)
	if err != nil {
		return nil, VSphereAPIError{UnderlyingError: err}
	}

	if vmRef == nil {
		return nil, VirtualMachineNotFoundError{Path: i.paths.VMPath, ID: id}
	}

	vm, ok := vmRef.(*object.VirtualMachine)
	if !ok {
		return nil, fmt.Errorf("object at path %s is a %T, but expected VirtualMachine", i.paths.VMPath+id, vmRef)
	}

	return i.instanceForVirtualMachine(ctx, vm)
}

func (i *vSphereInstanceManager) List(ctx context.Context) ([]*Instance, error) {
	folder, err := i.vmFolder(ctx)
	if err != nil {
		return nil, err
	}

	vms, err := folder.Children(ctx)
	if err != nil {
		return nil, VSphereAPIError{UnderlyingError: err}
	}

	var instances []*Instance
	for _, vmRef := range vms {
		vm, ok := vmRef.(*object.VirtualMachine)
		if !ok {
			continue
		}

		instance, err := i.instanceForVirtualMachine(ctx, vm)
		if err != nil {
			return nil, err
		}

		instances = append(instances, instance)
	}

	return instances, nil
}

func (i *vSphereInstanceManager) Start(ctx context.Context, base string) (*Instance, error) {
	client, err := i.client(ctx)
	if err != nil {
		return nil, err
	}

	searchIndex := object.NewSearchIndex(client.Client)

	vmRef, err := searchIndex.FindByInventoryPath(ctx, i.paths.BasePath+base)
	if err != nil {
		return nil, err
	}

	if vmRef == nil {
		return nil, BaseVirtualMachineNotFoundError{Path: i.paths.BasePath, Name: base}
	}

	vm, ok := vmRef.(*object.VirtualMachine)
	if !ok {
		return nil, fmt.Errorf("base VM %s is a %T, but expected VirtualMachine", base, vmRef)
	}

	var mvm mo.VirtualMachine
	err = vm.Properties(ctx, vm.Reference(), []string{"snapshot"}, &mvm)
	if err != nil {
		return nil, fmt.Errorf("couldn't get snapshot info for base VM: %s", err)
	}

	if mvm.Snapshot == nil {
		return nil, fmt.Errorf("invalid base VM (no snapshot)")
	}

	snapshotTree, ok := i.findSnapshot(mvm.Snapshot.RootSnapshotList, "base")
	if !ok {
		return nil, fmt.Errorf("invalid base VM (no snapshot)")
	}

	resourcePool, err := i.resourcePool(ctx)
	if err != nil {
		return nil, fmt.Errorf("couldn't get resource pool: %s", err)
	}

	relocateSpec := types.VirtualMachineRelocateSpec{
		DiskMoveType: string(types.VirtualMachineRelocateDiskMoveOptionsCreateNewChildDiskBacking),
		Pool:         resourcePool,
	}

	cloneSpec := types.VirtualMachineCloneSpec{
		Location: relocateSpec,
		PowerOn:  true,
		Template: false,
		Snapshot: &snapshotTree.Snapshot,
	}

	name := uuid.New()

	vmFolder, err := i.vmFolder(ctx)
	if err != nil {
		return nil, err
	}

	task, err := vm.Clone(ctx, vmFolder, name, cloneSpec)
	if err != nil {
		return nil, err
	}

	err = task.Wait(ctx)
	if err != nil {
		return nil, err
	}

	var mt mo.Task
	err = task.Properties(ctx, task.Reference(), []string{"info"}, &mt)
	if err != nil {
		return nil, err
	}

	if mt.Info.Result == nil {
		return nil, fmt.Errorf("expected VM, but got nil")
	}

	vmManagedRef, ok := mt.Info.Result.(types.ManagedObjectReference)
	if !ok {
		return nil, fmt.Errorf("expected ManagedObjectReference, but got %T", mt.Info.Result)
	}

	return i.instanceForVirtualMachine(ctx, object.NewVirtualMachine(client.Client, vmManagedRef))
}

func (i *vSphereInstanceManager) Terminate(ctx context.Context, id string) error {
	client, err := i.client(ctx)
	if err != nil {
		return err
	}

	searchIndex := object.NewSearchIndex(client.Client)

	vmRef, err := searchIndex.FindByInventoryPath(ctx, i.paths.VMPath+id)
	if err != nil {
		return err
	}

	if vmRef == nil {
		return VirtualMachineNotFoundError{Path: i.paths.VMPath, ID: id}
	}

	vm, ok := vmRef.(*object.VirtualMachine)
	if !ok {
		return fmt.Errorf("not a VM")
	}

	task, err := vm.PowerOff(ctx)
	if err != nil {
		return err
	}

	err = task.Wait(ctx)
	if err != nil {
		return err
	}

	task, err = vm.Destroy(ctx)
	if err != nil {
		return err
	}

	return task.Wait(ctx)
}

func (i *vSphereInstanceManager) client(ctx context.Context) (*govmomi.Client, error) {
	i.vSphereClientMutex.Lock()
	defer i.vSphereClientMutex.Unlock()

	if i.vSphereClient == nil {
		client, err := govmomi.NewClient(ctx, i.vSphereURL, true)
		if err != nil {
			return nil, fmt.Errorf("couldn't create vSphere client: %s", err)
		}

		i.vSphereClient = client
		return i.vSphereClient, nil
	}

	active, err := i.vSphereClient.SessionManager.SessionIsActive(ctx)
	if err != nil {
		client, err := govmomi.NewClient(ctx, i.vSphereURL, true)
		if err != nil {
			return nil, fmt.Errorf("couldn't create vSphere client: %s", err)
		}

		i.vSphereClient = client
		return i.vSphereClient, nil
	}

	if !active {
		err := i.vSphereClient.SessionManager.Login(ctx, i.vSphereURL.User)
		if err != nil {
			return nil, fmt.Errorf("couldn't log in to vSphere API: %s", err)
		}
	}

	return i.vSphereClient, nil
}

func (i *vSphereInstanceManager) vmFolder(ctx context.Context) (*object.Folder, error) {
	client, err := i.client(ctx)
	if err != nil {
		return nil, err
	}

	searchIndex := object.NewSearchIndex(client.Client)

	folderRef, err := searchIndex.FindByInventoryPath(ctx, i.paths.VMPath)
	if err != nil {
		return nil, err
	}

	if folderRef == nil {
		return nil, fmt.Errorf("VM folder not found")
	}

	folder, ok := folderRef.(*object.Folder)
	if !ok {
		return nil, fmt.Errorf("VM folder is not a folder but %T", folderRef)
	}

	return folder, nil
}

func (i *vSphereInstanceManager) resourcePool(ctx context.Context) (*types.ManagedObjectReference, error) {
	client, err := i.client(ctx)
	if err != nil {
		return nil, err
	}

	searchIndex := object.NewSearchIndex(client.Client)

	clusterRef, err := searchIndex.FindByInventoryPath(ctx, i.paths.ClusterPath)
	if err != nil {
		return nil, err
	}

	if clusterRef == nil {
		return nil, fmt.Errorf("cluster not found at %s", i.paths.ClusterPath)
	}

	cluster, ok := clusterRef.(*object.ClusterComputeResource)
	if !ok {
		return nil, fmt.Errorf("object at %s is %T, but expected ComputeClusterResource", i.paths.ClusterPath, clusterRef)
	}

	var mccr mo.ClusterComputeResource
	err = cluster.Properties(ctx, cluster.Reference(), []string{"resourcePool"}, &mccr)
	if err != nil {
		return nil, err
	}

	return mccr.ResourcePool, nil
}

func (i *vSphereInstanceManager) findSnapshot(roots []types.VirtualMachineSnapshotTree, name string) (types.VirtualMachineSnapshotTree, bool) {
	for _, snapshotTree := range roots {
		if snapshotTree.Name == name {
			return snapshotTree, true
		}

		tree, ok := i.findSnapshot(snapshotTree.ChildSnapshotList, name)
		if ok {
			return tree, true
		}
	}

	return types.VirtualMachineSnapshotTree{}, false
}

func (i *vSphereInstanceManager) instanceForVirtualMachine(ctx context.Context, vm *object.VirtualMachine) (*Instance, error) {
	var mvm mo.VirtualMachine
	err := vm.Properties(ctx, vm.Reference(), []string{"config", "guest", "runtime"}, &mvm)
	if err != nil {
		return nil, err
	}

	var ipAddresses []string
	for _, nic := range mvm.Guest.Net {
		for _, ip := range nic.IpConfig.IpAddress {
			ipAddresses = append(ipAddresses, ip.IpAddress)
		}
	}

	return &Instance{
		ID:          mvm.Config.Name,
		IPAddresses: ipAddresses,
		State:       string(mvm.Runtime.PowerState),
	}, nil
}
<|endoftext|>


<commit_before>// Package waitmap implements a simple thread-safe map.
package waitmap

import (
	"sync"
)

// An entry in a WaitMap. Note that mutx and cond may legally be nil - this
// avoids heavy allocations at the cost of some code complexity.
type entry struct {
	mutx *sync.Mutex
	cond *sync.Cond
	data interface{}
	ok   bool
}

type WaitMap struct {
	lock *sync.Mutex
	ents map[interface{}]*entry
}

func New() *WaitMap {
	return &WaitMap{
		lock: new(sync.Mutex),
		ents: make(map[interface{}]*entry),
	}
}

// Retrieves the value mapped to by k. If no such value is yet available, waits
// until one is, and then returns that. Otherwise, it returns the value
// available at the time Get is called.
func (m *WaitMap) Get(k interface{}) interface{} {
	m.lock.Lock()
	e, ok := m.ents[k]
	if !ok {
		mutx := new(sync.Mutex)
		e = &entry{
			mutx: mutx,
			cond: sync.NewCond(mutx),
			data: nil,
			ok: false,
		}
		m.ents[k] = e
	}
	m.lock.Unlock()

	// If e.ok is true, e.data exists and can never cease to exist. We need
	// this check to avoid using a nil e.mutx. We could also actually check
	// e.mutx to see if it's nil, but this accomplishes the same thing and
	// will be slightly faster on average (since we will often avoid
	// unnecessarily messing with the mutex).
	if e.ok { return e.data }
	e.mutx.Lock()
	defer e.mutx.Unlock()
	e.cond.Wait()
	return e.data
}

// Maps the given key and value, waking any waiting calls to Get.
func (m *WaitMap) Set(k interface{}, v interface{}) {
	m.lock.Lock()
	defer m.lock.Unlock()
	e, ok := m.ents[k]
	if !ok {
		e := &entry{nil, nil, v, true}
		m.ents[k] = e
		return
	}
	e.data = v
	e.ok = true
	e.cond.Broadcast()
}

// Returns true if k is a key in the map.
func (m *WaitMap) Check(k interface{}) bool {
	m.lock.Lock()
	defer m.lock.Unlock()
	e, ok := m.ents[k]
	if !ok { return false }
	return e.ok
}

<commit_msg>Fixed bug<commit_after>// Package waitmap implements a simple thread-safe map.
package waitmap

import (
	"sync"
)

// An entry in a WaitMap. Note that mutx and cond may legally be nil - this
// avoids heavy allocations at the cost of some code complexity.
type entry struct {
	mutx *sync.Mutex
	cond *sync.Cond
	data interface{}
	ok   bool
}

type WaitMap struct {
	lock *sync.Mutex
	ents map[interface{}]*entry
}

func New() *WaitMap {
	return &WaitMap{
		lock: new(sync.Mutex),
		ents: make(map[interface{}]*entry),
	}
}

// Retrieves the value mapped to by k. If no such value is yet available, waits
// until one is, and then returns that. Otherwise, it returns the value
// available at the time Get is called.
func (m *WaitMap) Get(k interface{}) interface{} {
	m.lock.Lock()
	e, ok := m.ents[k]
	if !ok {
		mutx := new(sync.Mutex)
		e = &entry{
			mutx: mutx,
			cond: sync.NewCond(mutx),
			data: nil,
			ok: false,
		}
		m.ents[k] = e
	}
	m.lock.Unlock()

	// If e.ok is true, e.data exists and can never cease to exist. We need
	// this check to avoid using a nil e.mutx. We could also actually check
	// e.mutx to see if it's nil, but this accomplishes the same thing and
	// will be slightly faster on average (since we will often avoid
	// unnecessarily messing with the mutex).
	if e.ok { return e.data }
	e.mutx.Lock()
	defer e.mutx.Unlock()
	e.cond.Wait()
	return e.data
}

// Maps the given key and value, waking any waiting calls to Get.
func (m *WaitMap) Set(k interface{}, v interface{}) {
	m.lock.Lock()
	defer m.lock.Unlock()
	e, ok := m.ents[k]
	if !ok {
		e := &entry{nil, nil, v, true}
		m.ents[k] = e
		return
	}
	e.data = v
	if !e.ok {
		e.ok = true
		e.cond.Broadcast()
	}
}

// Returns true if k is a key in the map.
func (m *WaitMap) Check(k interface{}) bool {
	m.lock.Lock()
	defer m.lock.Unlock()
	e, ok := m.ents[k]
	if !ok { return false }
	return e.ok
}

<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"reflect"
	"time"

	api "github.com/armon/consul-api"
)

const (
	// The amount of time to do a blocking query for
	defaultWaitTime = 60 * time.Second
)

type Watcher struct {
	// config is a Consul Template Config object to be read by the watcher
	config *Config
}

// NewWatcher accepts a Config and creates a new Watcher.
func NewWatcher(config *Config) (*Watcher, error) {
	if config == nil {
		return nil, errors.New("cannot specify empty config")
	}

	watcher := &Watcher{
		config: config,
	}

	return watcher, nil
}

// Watch starts the Watcher process, querying the Consul API and rendering any
// configuration file changes.
func (w *Watcher) Watch() error {
	client, err := w.client()
	if err != nil {
		return err
	}

	views, templates, ctemplates, err := w.createViews()
	if err != nil {
		return err
	}

	doneCh := make(chan struct{})
	viewCh := w.waitForChanges(views, client, doneCh)

	if w.config.Once {
		// If we are running in "once" mode, quit after the first poll
		close(doneCh)
	} else {
		// Ensure we stop background polling when we quit the Watcher
		defer close(doneCh)
	}

	for {
		if w.config.Once && w.renderedAll(views) {
			// If we are in "once" mode and all the templates have been rendered,
			// exit gracefully
			return nil
		}

		select {
		case view := <-viewCh:
			for _, template := range view.Templates {
				if w.config.Once && template.Rendered() {
					// If we are in "once" mode, do not rendered the same template twice
					continue
				}

				deps := templates[template]
				context := &TemplateContext{
					Services:    make(map[string][]*Service),
					Keys:        make(map[string]string),
					KeyPrefixes: make(map[string][]*KeyPair),
				}

				// Continue if not all the required dependencies have been loaded
				// into the views
				if !w.ready(views, deps) {
					continue
				}

				for _, dep := range deps {
					v := views[dep]
					switch d := v.Dependency.(type) {
					case *ServiceDependency:
						context.Services[d.Key()] = v.Data.([]*Service)
					case *KeyDependency:
						context.Keys[d.Key()] = v.Data.(string)
					case *KeyPrefixDependency:
						context.KeyPrefixes[d.Key()] = v.Data.([]*KeyPair)
					default:
						panic(fmt.Sprintf("unknown dependency type: %q", d))
					}
				}

				if w.config.Dry {
					template.Execute(os.Stderr, context)
				} else {
					ctemplate := ctemplates[template]
					out, err := os.OpenFile(ctemplate.Destination, os.O_WRONLY, 0666)
					if err != nil {
						panic(err)
					}

					template.Execute(out, context)
				}
			}
		}
	}

	return nil
}

//
func (w *Watcher) waitForChanges(views map[Dependency]*DataView, client *api.Client, doneCh chan struct{}) <-chan *DataView {
	viewCh := make(chan *DataView, len(views))
	for _, view := range views {
		go view.poll(viewCh, client, doneCh)
	}
	return viewCh
}

//
func (w *Watcher) createViews() (map[Dependency]*DataView, map[*Template][]Dependency, map[*Template]*ConfigTemplate, error) {
	views := make(map[Dependency]*DataView)
	templates := make(map[*Template][]Dependency)
	ctemplates := make(map[*Template]*ConfigTemplate)

	// For each Dependency per ConfigTemplate, construct a DataView object which
	// ties the dependency to the Templates which depend on it
	for _, ctemplate := range w.config.ConfigTemplates {
		template := &Template{Input: ctemplate.Source}
		deps, err := template.Dependencies()
		if err != nil {
			return nil, nil, nil, err
		}

		for _, dep := range deps {
			// Create the DataView if it does not exist
			if _, ok := views[dep]; !ok {
				views[dep] = &DataView{Dependency: dep}
			}

			// Create the Templtes slice if it does not exist
			if len(views[dep].Templates) == 0 {
				views[dep].Templates = make([]*Template, 0, 1)
			}

			// Append the ConfigTemplate to the slice
			views[dep].Templates = append(views[dep].Templates, template)
		}

		// Add the template and its dependencies to the list
		templates[template] = deps

		// Map the template to its ConfigTemplate
		ctemplates[template] = ctemplate
	}

	return views, templates, ctemplates, nil
}

// ready determines if the views have loaded all the required information
// from the dependency.
func (w *Watcher) ready(views map[Dependency]*DataView, deps []Dependency) bool {
	for _, dep := range deps {
		v := views[dep]
		if !v.loaded() {
			return false
		}
	}
	return true
}

//
func (w *Watcher) renderedAll(views map[Dependency]*DataView) bool {
	for _, view := range views {
		for _, template := range view.Templates {
			if !template.Rendered() {
				return false
			}
		}
	}

	return true
}

//
func (w *Watcher) client() (*api.Client, error) {
	consulConfig := api.DefaultConfig()
	if w.config.Consul != "" {
		consulConfig.Address = w.config.Consul
	}
	if w.config.Token != "" {
		consulConfig.Token = w.config.Token
	}

	client, err := api.NewClient(consulConfig)
	if err != nil {
		return nil, err
	}
	if _, err := client.Agent().NodeName(); err != nil {
		return nil, err
	}

	return client, nil
}

/// ------------------------- ///

type DataView struct {
	Templates  []*Template
	Dependency Dependency
	Data       interface{}
	LastIndex  uint64
}

//
func (view *DataView) poll(ch chan *DataView, client *api.Client, doneCh chan struct{}) {
	for {
		options := &api.QueryOptions{
			WaitTime:  defaultWaitTime,
			WaitIndex: view.LastIndex,
		}
		data, qm, err := view.Dependency.Fetch(client, options)
		if err != nil {
			panic(err) // TODO: push err to err ch or something
		}

		// Consul is allowed to return even if there's no new data. Ignore data if
		// the index is the same.
		if qm.LastIndex == view.LastIndex {
			continue
		}

		// Update the index in case we got a new version, but the data is the same
		view.LastIndex = qm.LastIndex

		// Do not trigger a render if the data is the same
		if reflect.DeepEqual(data, view.Data) {
			continue
		}

		// If we got this far, there is new data!
		view.Data = data
		ch <- view

		// Break from the function if we are done - this happens at the end of the
		// function to ensure it runs at least once
		select {
		case <-doneCh:
			return
		}
	}
}

//
func (view *DataView) loaded() bool {
	return view.LastIndex != 0
}
<commit_msg>Add default value for poll() case<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"reflect"
	"time"

	api "github.com/armon/consul-api"
)

const (
	// The amount of time to do a blocking query for
	defaultWaitTime = 60 * time.Second
)

type Watcher struct {
	// config is a Consul Template Config object to be read by the watcher
	config *Config
}

// NewWatcher accepts a Config and creates a new Watcher.
func NewWatcher(config *Config) (*Watcher, error) {
	if config == nil {
		return nil, errors.New("cannot specify empty config")
	}

	watcher := &Watcher{
		config: config,
	}

	return watcher, nil
}

// Watch starts the Watcher process, querying the Consul API and rendering any
// configuration file changes.
func (w *Watcher) Watch() error {
	client, err := w.client()
	if err != nil {
		return err
	}

	views, templates, ctemplates, err := w.createViews()
	if err != nil {
		return err
	}

	doneCh := make(chan struct{})
	viewCh := w.waitForChanges(views, client, doneCh)

	if w.config.Once {
		// If we are running in "once" mode, quit after the first poll
		close(doneCh)
	} else {
		// Ensure we stop background polling when we quit the Watcher
		defer close(doneCh)
	}

	for {
		if w.config.Once && w.renderedAll(views) {
			// If we are in "once" mode and all the templates have been rendered,
			// exit gracefully
			return nil
		}

		select {
		case view := <-viewCh:
			for _, template := range view.Templates {
				if w.config.Once && template.Rendered() {
					// If we are in "once" mode, do not rendered the same template twice
					continue
				}

				deps := templates[template]
				context := &TemplateContext{
					Services:    make(map[string][]*Service),
					Keys:        make(map[string]string),
					KeyPrefixes: make(map[string][]*KeyPair),
				}

				// Continue if not all the required dependencies have been loaded
				// into the views
				if !w.ready(views, deps) {
					continue
				}

				for _, dep := range deps {
					v := views[dep]
					switch d := v.Dependency.(type) {
					case *ServiceDependency:
						context.Services[d.Key()] = v.Data.([]*Service)
					case *KeyDependency:
						context.Keys[d.Key()] = v.Data.(string)
					case *KeyPrefixDependency:
						context.KeyPrefixes[d.Key()] = v.Data.([]*KeyPair)
					default:
						panic(fmt.Sprintf("unknown dependency type: %q", d))
					}
				}

				if w.config.Dry {
					template.Execute(os.Stderr, context)
				} else {
					ctemplate := ctemplates[template]
					out, err := os.OpenFile(ctemplate.Destination, os.O_WRONLY, 0666)
					if err != nil {
						panic(err)
					}

					template.Execute(out, context)
				}
			}
		}
	}

	return nil
}

//
func (w *Watcher) waitForChanges(views map[Dependency]*DataView, client *api.Client, doneCh chan struct{}) <-chan *DataView {
	viewCh := make(chan *DataView, len(views))
	for _, view := range views {
		go view.poll(viewCh, client, doneCh)
	}
	return viewCh
}

//
func (w *Watcher) createViews() (map[Dependency]*DataView, map[*Template][]Dependency, map[*Template]*ConfigTemplate, error) {
	views := make(map[Dependency]*DataView)
	templates := make(map[*Template][]Dependency)
	ctemplates := make(map[*Template]*ConfigTemplate)

	// For each Dependency per ConfigTemplate, construct a DataView object which
	// ties the dependency to the Templates which depend on it
	for _, ctemplate := range w.config.ConfigTemplates {
		template := &Template{Input: ctemplate.Source}
		deps, err := template.Dependencies()
		if err != nil {
			return nil, nil, nil, err
		}

		for _, dep := range deps {
			// Create the DataView if it does not exist
			if _, ok := views[dep]; !ok {
				views[dep] = &DataView{Dependency: dep}
			}

			// Create the Templtes slice if it does not exist
			if len(views[dep].Templates) == 0 {
				views[dep].Templates = make([]*Template, 0, 1)
			}

			// Append the ConfigTemplate to the slice
			views[dep].Templates = append(views[dep].Templates, template)
		}

		// Add the template and its dependencies to the list
		templates[template] = deps

		// Map the template to its ConfigTemplate
		ctemplates[template] = ctemplate
	}

	return views, templates, ctemplates, nil
}

// ready determines if the views have loaded all the required information
// from the dependency.
func (w *Watcher) ready(views map[Dependency]*DataView, deps []Dependency) bool {
	for _, dep := range deps {
		v := views[dep]
		if !v.loaded() {
			return false
		}
	}
	return true
}

//
func (w *Watcher) renderedAll(views map[Dependency]*DataView) bool {
	for _, view := range views {
		for _, template := range view.Templates {
			if !template.Rendered() {
				return false
			}
		}
	}

	return true
}

//
func (w *Watcher) client() (*api.Client, error) {
	consulConfig := api.DefaultConfig()
	if w.config.Consul != "" {
		consulConfig.Address = w.config.Consul
	}
	if w.config.Token != "" {
		consulConfig.Token = w.config.Token
	}

	client, err := api.NewClient(consulConfig)
	if err != nil {
		return nil, err
	}
	if _, err := client.Agent().NodeName(); err != nil {
		return nil, err
	}

	return client, nil
}

/// ------------------------- ///

type DataView struct {
	Templates  []*Template
	Dependency Dependency
	Data       interface{}
	LastIndex  uint64
}

//
func (view *DataView) poll(ch chan *DataView, client *api.Client, doneCh chan struct{}) {
	for {
		options := &api.QueryOptions{
			WaitTime:  defaultWaitTime,
			WaitIndex: view.LastIndex,
		}
		data, qm, err := view.Dependency.Fetch(client, options)
		if err != nil {
			panic(err) // TODO: push err to err ch or something
		}

		// Consul is allowed to return even if there's no new data. Ignore data if
		// the index is the same.
		if qm.LastIndex == view.LastIndex {
			continue
		}

		// Update the index in case we got a new version, but the data is the same
		view.LastIndex = qm.LastIndex

		// Do not trigger a render if the data is the same
		if reflect.DeepEqual(data, view.Data) {
			continue
		}

		// If we got this far, there is new data!
		view.Data = data
		ch <- view

		// Break from the function if we are done - this happens at the end of the
		// function to ensure it runs at least once
		select {
		case <-doneCh:
			return
		default:
			continue
		}
	}
}

//
func (view *DataView) loaded() bool {
	return view.LastIndex != 0
}
<|endoftext|>


<commit_before>package web

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"time"

	"github.com/codegangsta/negroni"
	"github.com/jingweno/negroni-gorelic"
	"github.com/sjltaylor/stats-gopher/mq"
	"github.com/sjltaylor/stats-gopher/presence"
)

var monitors = presence.NewMonitorPool(map[string]time.Duration{
	"heartbeat":     time.Second * 45,
	"user-activity": time.Minute * 45,
})

// Start the web server
// the key is the api key for new relic monitoring
func Start(bind, key string) {
	mux := http.NewServeMux()

	mux.HandleFunc("/gopher/", gopherEndpoint)
	mux.HandleFunc("/presence/", presenceEndpoint)
	mux.HandleFunc("/", helloEndpoint)

	n := negroni.Classic()
	n.UseHandler(mux)

	n.Use(negroni.NewRecovery())

	if key != "" {
		n.Use(negronigorelic.New(key, "stats-gopher", true))
	}

	go func() {
		for {
			n := <-monitors.C
			event := map[string]interface{}{
				"key":              n.Key,
				"code":             n.Code,
				"start":            n.Start,
				"lastNotification": n.LastNotification,
				"end":              n.End,
				"duration":         n.Duration,
				"wait":             n.Wait,
			}
			mq.Send(event)
		}
	}()

	n.Run(bind)
}

func helloEndpoint(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(405)
		return
	}

	respond("hello, send me something", w, r)
}

func gopherEndpoint(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "POST, OPTIONS")

	if r.Method == "OPTIONS" {
		w.WriteHeader(200)
		return
	}

	if r.Method != "POST" {
		w.WriteHeader(405)
		return
	}

	var blob []byte
	var err error
	var data interface{}

	if blob, err = ioutil.ReadAll(r.Body); err != nil {
		log.Println(fmt.Sprintf("web: error reading body: %s", err))
		w.WriteHeader(500)
		return
	}

	if err = json.Unmarshal(blob, &data); err != nil {
		log.Printf("web: error parsing json (%s): %s\n", err, string(blob))
		w.WriteHeader(500)
		return
	}

	go mq.Send(data)

	respond("nom nom nom", w, r)
}

func presenceEndpoint(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "POST, OPTIONS")

	if r.Method == "OPTIONS" {
		w.WriteHeader(200)
		return
	}

	if r.Method != "POST" {
		w.WriteHeader(405)
		return
	}

	var blob []byte
	var err error
	var notifications []presence.Notification

	if blob, err = ioutil.ReadAll(r.Body); err != nil {
		log.Println(fmt.Sprintf("web: error reading heartbeat body: %s", err))
		w.WriteHeader(500)
		return
	}

	if err = json.Unmarshal(blob, &notifications); err != nil {
		log.Printf("web: error parsing heartbeat json (%s): %s\n", err, string(blob))
		w.WriteHeader(500)
		return
	}

	for _, notification := range notifications {
		go monitors.Notify(&notification)
	}
}

func respond(message string, w http.ResponseWriter, r *http.Request) {
	if _, err := fmt.Fprint(w, fmt.Sprintf("%s\n", message)); err != nil {
		w.WriteHeader(500)
		log.Printf(fmt.Sprintf("web: couldn't write to the response: '%s', %s\n", r.URL.String(), err))
	}
}
<commit_msg>put an eventType in, else insights won't have it<commit_after>package web

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"time"

	"github.com/codegangsta/negroni"
	"github.com/jingweno/negroni-gorelic"
	"github.com/sjltaylor/stats-gopher/mq"
	"github.com/sjltaylor/stats-gopher/presence"
)

var monitors = presence.NewMonitorPool(map[string]time.Duration{
	"heartbeat":     time.Second * 45,
	"user-activity": time.Minute * 45,
})

// Start the web server
// the key is the api key for new relic monitoring
func Start(bind, key string) {
	mux := http.NewServeMux()

	mux.HandleFunc("/gopher/", gopherEndpoint)
	mux.HandleFunc("/presence/", presenceEndpoint)
	mux.HandleFunc("/", helloEndpoint)

	n := negroni.Classic()
	n.UseHandler(mux)

	n.Use(negroni.NewRecovery())

	if key != "" {
		n.Use(negronigorelic.New(key, "stats-gopher", true))
	}

	go func() {
		for {
			n := <-monitors.C
			event := map[string]interface{}{
				"eventType":        "UserAction",
				"key":              n.Key,
				"code":             n.Code,
				"start":            n.Start,
				"lastNotification": n.LastNotification,
				"end":              n.End,
				"duration":         n.Duration,
				"wait":             n.Wait,
			}
			mq.Send(event)
		}
	}()

	n.Run(bind)
}

func helloEndpoint(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(405)
		return
	}

	respond("hello, send me something", w, r)
}

func gopherEndpoint(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "POST, OPTIONS")

	if r.Method == "OPTIONS" {
		w.WriteHeader(200)
		return
	}

	if r.Method != "POST" {
		w.WriteHeader(405)
		return
	}

	var blob []byte
	var err error
	var data interface{}

	if blob, err = ioutil.ReadAll(r.Body); err != nil {
		log.Println(fmt.Sprintf("web: error reading body: %s", err))
		w.WriteHeader(500)
		return
	}

	if err = json.Unmarshal(blob, &data); err != nil {
		log.Printf("web: error parsing json (%s): %s\n", err, string(blob))
		w.WriteHeader(500)
		return
	}

	go mq.Send(data)

	respond("nom nom nom", w, r)
}

func presenceEndpoint(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "POST, OPTIONS")

	if r.Method == "OPTIONS" {
		w.WriteHeader(200)
		return
	}

	if r.Method != "POST" {
		w.WriteHeader(405)
		return
	}

	var blob []byte
	var err error
	var notifications []presence.Notification

	if blob, err = ioutil.ReadAll(r.Body); err != nil {
		log.Println(fmt.Sprintf("web: error reading heartbeat body: %s", err))
		w.WriteHeader(500)
		return
	}

	if err = json.Unmarshal(blob, &notifications); err != nil {
		log.Printf("web: error parsing heartbeat json (%s): %s\n", err, string(blob))
		w.WriteHeader(500)
		return
	}

	for _, notification := range notifications {
		go monitors.Notify(&notification)
	}
}

func respond(message string, w http.ResponseWriter, r *http.Request) {
	if _, err := fmt.Fprint(w, fmt.Sprintf("%s\n", message)); err != nil {
		w.WriteHeader(500)
		log.Printf(fmt.Sprintf("web: couldn't write to the response: '%s', %s\n", r.URL.String(), err))
	}
}
<|endoftext|>


<commit_before>package corehttp

import (
	"errors"
	"fmt"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"

	oldcmds "github.com/ipfs/go-ipfs/commands"
	core "github.com/ipfs/go-ipfs/core"
	corecommands "github.com/ipfs/go-ipfs/core/commands"
	path "github.com/ipfs/go-ipfs/path"
	config "github.com/ipfs/go-ipfs/repo/config"

	cmds "gx/ipfs/QmUEB5nT4LG3TkUd5mkHrfRESUSgaUD4r7jSAYvvPeuWT9/go-ipfs-cmds"
	cmdsHttp "gx/ipfs/QmUEB5nT4LG3TkUd5mkHrfRESUSgaUD4r7jSAYvvPeuWT9/go-ipfs-cmds/http"
)

var (
	errAPIVersionMismatch = errors.New("api version mismatch")
)

const originEnvKey = "API_ORIGIN"
const originEnvKeyDeprecate = `You are using the ` + originEnvKey + `ENV Variable.
This functionality is deprecated, and will be removed in future versions.
Instead, try either adding headers to the config, or passing them via
cli arguments:

	ipfs config API.HTTPHeaders 'Access-Control-Allow-Origin' '*'
	ipfs daemon

or

	ipfs daemon --api-http-header 'Access-Control-Allow-Origin: *'
`

// APIPath is the path at which the API is mounted.
const APIPath = "/api/v0"

var defaultLocalhostOrigins = []string{
	"http://127.0.0.1:<port>",
	"https://127.0.0.1:<port>",
	"http://localhost:<port>",
	"https://localhost:<port>",
}

func addCORSFromEnv(c *cmdsHttp.ServerConfig) {
	origin := os.Getenv(originEnvKey)
	if origin != "" {
		log.Warning(originEnvKeyDeprecate)
		if len(c.AllowedOrigins()) == 0 {
			c.SetAllowedOrigins([]string{origin}...)
		}
		c.AppendAllowedOrigins(origin)
	}
}

func addHeadersFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) {
	log.Info("Using API.HTTPHeaders:", nc.API.HTTPHeaders)

	if acao := nc.API.HTTPHeaders[cmdsHttp.ACAOrigin]; acao != nil {
		c.SetAllowedOrigins(acao...)
	}
	if acam := nc.API.HTTPHeaders[cmdsHttp.ACAMethods]; acam != nil {
		c.SetAllowedMethods(acam...)
	}
	if acac := nc.API.HTTPHeaders[cmdsHttp.ACACredentials]; acac != nil {
		for _, v := range acac {
			c.SetAllowCredentials(strings.ToLower(v) == "true")
		}
	}

	c.Headers = nc.API.HTTPHeaders
	c.Headers["Server"] = []string{"go-ipfs/" + config.CurrentVersionNumber}
}

func addCORSDefaults(c *cmdsHttp.ServerConfig) {
	// by default use localhost origins
	if len(c.AllowedOrigins()) == 0 {
		c.SetAllowedOrigins(defaultLocalhostOrigins...)
	}

	// by default, use GET, PUT, POST
	if len(c.AllowedMethods()) == 0 {
		c.SetAllowedMethods("GET", "POST", "PUT")
	}
}

func patchCORSVars(c *cmdsHttp.ServerConfig, addr net.Addr) {

	// we have to grab the port from an addr, which may be an ip6 addr.
	// TODO: this should take multiaddrs and derive port from there.
	port := ""
	if tcpaddr, ok := addr.(*net.TCPAddr); ok {
		port = strconv.Itoa(tcpaddr.Port)
	} else if udpaddr, ok := addr.(*net.UDPAddr); ok {
		port = strconv.Itoa(udpaddr.Port)
	}

	// we're listening on tcp/udp with ports. ("udp!?" you say? yeah... it happens...)
	origins := c.AllowedOrigins()
	for i, o := range origins {
		// TODO: allow replacing <host>. tricky, ip4 and ip6 and hostnames...
		if port != "" {
			o = strings.Replace(o, "<port>", port, -1)
		}
		origins[i] = o
	}
	c.SetAllowedOrigins(origins...)
}

func commandsOption(cctx oldcmds.Context, command *cmds.Command) ServeOption {
	return func(n *core.IpfsNode, l net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {

		cfg := cmdsHttp.NewServerConfig()
		cfg.SetAllowedMethods("GET", "POST", "PUT")
		cfg.APIPath = APIPath
		rcfg, err := n.Repo.Config()
		if err != nil {
			return nil, err
		}

		addHeadersFromConfig(cfg, rcfg)
		addCORSFromEnv(cfg)
		addCORSDefaults(cfg)
		patchCORSVars(cfg, l.Addr())

		cmdHandler := cmdsHttp.NewHandler(&cctx, command, cfg)
		mux.Handle(APIPath+"/", cmdHandler)
		return mux, nil
	}
}

// CommandsOption constructs a ServerOption for hooking the commands into the
// HTTP server.
func CommandsOption(cctx oldcmds.Context) ServeOption {
	return commandsOption(cctx, corecommands.Root)
}

// CommandsROOption constructs a ServerOption for hooking the read-only commands
// into the HTTP server.
func CommandsROOption(cctx oldcmds.Context) ServeOption {
	return commandsOption(cctx, corecommands.RootRO)
}

// CheckVersionOption returns a ServeOption that checks whether the client ipfs version matches. Does nothing when the user agent string does not contain `/go-ipfs/`
func CheckVersionOption() ServeOption {
	daemonVersion := config.ApiVersion

	return ServeOption(func(n *core.IpfsNode, l net.Listener, parent *http.ServeMux) (*http.ServeMux, error) {
		mux := http.NewServeMux()
		parent.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			if strings.HasPrefix(r.URL.Path, APIPath) {
				cmdqry := r.URL.Path[len(APIPath):]
				pth := path.SplitList(cmdqry)

				// backwards compatibility to previous version check
				if pth[1] != "version" {
					clientVersion := r.UserAgent()
					// skips check if client is not go-ipfs
					if clientVersion != "" && strings.Contains(clientVersion, "/go-ipfs/") && daemonVersion != clientVersion {
						http.Error(w, fmt.Sprintf("%s (%s != %s)", errAPIVersionMismatch, daemonVersion, clientVersion), http.StatusBadRequest)
						return
					}
				}
			}

			mux.ServeHTTP(w, r)
		})

		return mux, nil
	})
}
<commit_msg>fix a race and a potential race in http options<commit_after>package corehttp

import (
	"errors"
	"fmt"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"

	oldcmds "github.com/ipfs/go-ipfs/commands"
	core "github.com/ipfs/go-ipfs/core"
	corecommands "github.com/ipfs/go-ipfs/core/commands"
	path "github.com/ipfs/go-ipfs/path"
	config "github.com/ipfs/go-ipfs/repo/config"

	cmds "gx/ipfs/QmUEB5nT4LG3TkUd5mkHrfRESUSgaUD4r7jSAYvvPeuWT9/go-ipfs-cmds"
	cmdsHttp "gx/ipfs/QmUEB5nT4LG3TkUd5mkHrfRESUSgaUD4r7jSAYvvPeuWT9/go-ipfs-cmds/http"
)

var (
	errAPIVersionMismatch = errors.New("api version mismatch")
)

const originEnvKey = "API_ORIGIN"
const originEnvKeyDeprecate = `You are using the ` + originEnvKey + `ENV Variable.
This functionality is deprecated, and will be removed in future versions.
Instead, try either adding headers to the config, or passing them via
cli arguments:

	ipfs config API.HTTPHeaders 'Access-Control-Allow-Origin' '*'
	ipfs daemon

or

	ipfs daemon --api-http-header 'Access-Control-Allow-Origin: *'
`

// APIPath is the path at which the API is mounted.
const APIPath = "/api/v0"

var defaultLocalhostOrigins = []string{
	"http://127.0.0.1:<port>",
	"https://127.0.0.1:<port>",
	"http://localhost:<port>",
	"https://localhost:<port>",
}

func addCORSFromEnv(c *cmdsHttp.ServerConfig) {
	origin := os.Getenv(originEnvKey)
	if origin != "" {
		log.Warning(originEnvKeyDeprecate)
		if len(c.AllowedOrigins()) == 0 {
			c.SetAllowedOrigins([]string{origin}...)
		}
		c.AppendAllowedOrigins(origin)
	}
}

func addHeadersFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) {
	log.Info("Using API.HTTPHeaders:", nc.API.HTTPHeaders)

	if acao := nc.API.HTTPHeaders[cmdsHttp.ACAOrigin]; acao != nil {
		c.SetAllowedOrigins(acao...)
	}
	if acam := nc.API.HTTPHeaders[cmdsHttp.ACAMethods]; acam != nil {
		c.SetAllowedMethods(acam...)
	}
	if acac := nc.API.HTTPHeaders[cmdsHttp.ACACredentials]; acac != nil {
		for _, v := range acac {
			c.SetAllowCredentials(strings.ToLower(v) == "true")
		}
	}

	c.Headers = make(map[string][]string, len(nc.API.HTTPHeaders))

	// Copy these because the config is shared and this function is called
	// in multiple places concurrently. Updating these in-place *is* racy.
	for h, v := range nc.API.HTTPHeaders {
		c.Headers[h] = v
	}
	c.Headers["Server"] = []string{"go-ipfs/" + config.CurrentVersionNumber}
}

func addCORSDefaults(c *cmdsHttp.ServerConfig) {
	// by default use localhost origins
	if len(c.AllowedOrigins()) == 0 {
		c.SetAllowedOrigins(defaultLocalhostOrigins...)
	}

	// by default, use GET, PUT, POST
	if len(c.AllowedMethods()) == 0 {
		c.SetAllowedMethods("GET", "POST", "PUT")
	}
}

func patchCORSVars(c *cmdsHttp.ServerConfig, addr net.Addr) {

	// we have to grab the port from an addr, which may be an ip6 addr.
	// TODO: this should take multiaddrs and derive port from there.
	port := ""
	if tcpaddr, ok := addr.(*net.TCPAddr); ok {
		port = strconv.Itoa(tcpaddr.Port)
	} else if udpaddr, ok := addr.(*net.UDPAddr); ok {
		port = strconv.Itoa(udpaddr.Port)
	}

	// we're listening on tcp/udp with ports. ("udp!?" you say? yeah... it happens...)
	oldOrigins := c.AllowedOrigins()
	newOrigins := make([]string, len(oldOrigins))
	for i, o := range oldOrigins {
		// TODO: allow replacing <host>. tricky, ip4 and ip6 and hostnames...
		if port != "" {
			o = strings.Replace(o, "<port>", port, -1)
		}
		newOrigins[i] = o
	}
	c.SetAllowedOrigins(newOrigins...)
}

func commandsOption(cctx oldcmds.Context, command *cmds.Command) ServeOption {
	return func(n *core.IpfsNode, l net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {

		cfg := cmdsHttp.NewServerConfig()
		cfg.SetAllowedMethods("GET", "POST", "PUT")
		cfg.APIPath = APIPath
		rcfg, err := n.Repo.Config()
		if err != nil {
			return nil, err
		}

		addHeadersFromConfig(cfg, rcfg)
		addCORSFromEnv(cfg)
		addCORSDefaults(cfg)
		patchCORSVars(cfg, l.Addr())

		cmdHandler := cmdsHttp.NewHandler(&cctx, command, cfg)
		mux.Handle(APIPath+"/", cmdHandler)
		return mux, nil
	}
}

// CommandsOption constructs a ServerOption for hooking the commands into the
// HTTP server.
func CommandsOption(cctx oldcmds.Context) ServeOption {
	return commandsOption(cctx, corecommands.Root)
}

// CommandsROOption constructs a ServerOption for hooking the read-only commands
// into the HTTP server.
func CommandsROOption(cctx oldcmds.Context) ServeOption {
	return commandsOption(cctx, corecommands.RootRO)
}

// CheckVersionOption returns a ServeOption that checks whether the client ipfs version matches. Does nothing when the user agent string does not contain `/go-ipfs/`
func CheckVersionOption() ServeOption {
	daemonVersion := config.ApiVersion

	return ServeOption(func(n *core.IpfsNode, l net.Listener, parent *http.ServeMux) (*http.ServeMux, error) {
		mux := http.NewServeMux()
		parent.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			if strings.HasPrefix(r.URL.Path, APIPath) {
				cmdqry := r.URL.Path[len(APIPath):]
				pth := path.SplitList(cmdqry)

				// backwards compatibility to previous version check
				if pth[1] != "version" {
					clientVersion := r.UserAgent()
					// skips check if client is not go-ipfs
					if clientVersion != "" && strings.Contains(clientVersion, "/go-ipfs/") && daemonVersion != clientVersion {
						http.Error(w, fmt.Sprintf("%s (%s != %s)", errAPIVersionMismatch, daemonVersion, clientVersion), http.StatusBadRequest)
						return
					}
				}
			}

			mux.ServeHTTP(w, r)
		})

		return mux, nil
	})
}
<|endoftext|>


<commit_before>package txbuilder

import (
	"context"
	"encoding/json"

	"golang.org/x/crypto/sha3"

	"chain/crypto/ed25519"
	chainjson "chain/encoding/json"
	"chain/errors"
	"chain/protocol/bc"
	"chain/protocol/vm"
	"chain/protocol/vmutil"
)

// WitnessComponent encodes instructions for finalizing a transaction
// by populating its InputWitness fields. Each WitnessComponent object
// produces zero or more items for the InputWitness of the txinput it
// corresponds to.
type WitnessComponent interface {
	// Sign is called to add signatures. Actual signing is delegated to
	// a callback function.
	Sign(context.Context, *Template, int, []string, func(context.Context, string, []uint32, [32]byte) ([]byte, error)) error

	// Materialize is called to turn the component into a vector of
	// arguments for the input witness.
	Materialize(*Template, int) ([][]byte, error)
}

// materializeWitnesses takes a filled in Template and "materializes"
// each witness component, turning it into a vector of arguments for
// the tx's input witness, creating a fully-signed transaction.
func materializeWitnesses(txTemplate *Template) error {
	msg := txTemplate.Transaction

	if msg == nil {
		return errors.Wrap(ErrMissingRawTx)
	}

	if len(txTemplate.SigningInstructions) > len(msg.Inputs) {
		return errors.Wrap(ErrBadInstructionCount)
	}

	for i, sigInst := range txTemplate.SigningInstructions {
		if msg.Inputs[sigInst.Position] == nil {
			return errors.WithDetailf(ErrBadTxInputIdx, "signing instruction %d references missing tx input %d", i, sigInst.Position)
		}

		var witness [][]byte
		for j, c := range sigInst.WitnessComponents {
			items, err := c.Materialize(txTemplate, i)
			if err != nil {
				return errors.WithDetailf(err, "error in witness component %d of input %d", j, i)
			}
			witness = append(witness, items...)
		}

		msg.Inputs[sigInst.Position].InputWitness = witness
	}

	return nil
}

type (
	SignatureWitness struct {
		// Quorum is the number of signatures required.
		Quorum int `json:"quorum"`

		// Keys are the identities of the keys to sign with.
		Keys []KeyID `json:"keys"`

		// Program is the deferred predicate, whose hash is what gets
		// signed. If empty, it is computed during Sign from the outputs
		// and the current input of the transaction.
		Program chainjson.HexBytes `json:"program"`

		// Sigs are signatures of Program made from each of the Keys
		// during Sign.
		Sigs []chainjson.HexBytes `json:"signatures"`
	}

	KeyID struct {
		XPub           string   `json:"xpub"`
		DerivationPath []uint32 `json:"derivation_path"`
	}
)

var ErrEmptyProgram = errors.New("empty signature program")

// Sign populates sw.Sigs with as many signatures of the deferred predicate in
// sw.Program as it can from the set of keys in sw.Keys.
//
// If sw.Program is empty, it is populated with an _inferred_ deferred
// predicate: a program committing to aspects of the current
// transaction. Specifically, the program commits to:
//  - the mintime and maxtime of the transaction (if non-zero)
//  - the outpoint and (if non-empty) reference data of the current input
//  - the assetID, amount, control program, and (if non-empty) reference data of each output.
func (sw *SignatureWitness) Sign(ctx context.Context, tpl *Template, index int, xpubs []string, signFn func(context.Context, string, []uint32, [32]byte) ([]byte, error)) error {
	// Compute the deferred predicate to sign. This is either a
	// txsighash program if tpl.Final is true (i.e., the tx is complete
	// and no further changes are allowed) or a program enforcing
	// constraints derived from the existing outputs and current input.
	if len(sw.Program) == 0 {
		sw.Program = buildSigProgram(tpl, index)
		if len(sw.Program) == 0 {
			return ErrEmptyProgram
		}
	}
	if len(sw.Sigs) < len(sw.Keys) {
		// Each key in sw.Keys may produce a signature in sw.Sigs. Make
		// sure there are enough slots in sw.Sigs and that we preserve any
		// sigs already present.
		newSigs := make([]chainjson.HexBytes, len(sw.Keys))
		copy(newSigs, sw.Sigs)
		sw.Sigs = newSigs
	}
	h := sha3.Sum256(sw.Program)
	for i, keyID := range sw.Keys {
		if len(sw.Sigs[i]) > 0 {
			// Already have a signature for this key
			continue
		}
		if !contains(xpubs, keyID.XPub) {
			continue
		}
		sigBytes, err := signFn(ctx, keyID.XPub, keyID.DerivationPath, h)
		if err != nil {
			return errors.WithDetailf(err, "computing signature %d", i)
		}
		sw.Sigs[i] = sigBytes
	}
	return nil
}

func contains(list []string, key string) bool {
	for _, k := range list {
		if k == key {
			return true
		}
	}
	return false
}

func buildSigProgram(tpl *Template, index int) []byte {
	if tpl.Final {
		h := tpl.Hash(index)
		builder := vmutil.NewBuilder()
		builder.AddData(h[:])
		builder.AddOp(vm.OP_TXSIGHASH).AddOp(vm.OP_EQUAL)
		return builder.Program
	}
	constraints := make([]constraint, 0, 3+len(tpl.Transaction.Outputs))
	constraints = append(constraints, &timeConstraint{
		minTimeMS: tpl.Transaction.MinTime,
		maxTimeMS: tpl.Transaction.MaxTime,
	})
	inp := tpl.Transaction.Inputs[index]
	if !inp.IsIssuance() {
		constraints = append(constraints, outpointConstraint(inp.Outpoint()))
	}
	if len(inp.ReferenceData) > 0 {
		constraints = append(constraints, refdataConstraint(inp.ReferenceData))
	}
	for _, out := range tpl.Transaction.Outputs {
		c := &payConstraint{
			AssetAmount: out.AssetAmount,
			Program:     out.ControlProgram,
		}
		if len(out.ReferenceData) > 0 {
			h := sha3.Sum256(out.ReferenceData)
			c.RefDataHash = (*bc.Hash)(&h)
		}
		constraints = append(constraints, c)
	}
	var program []byte
	for i, c := range constraints {
		program = append(program, c.code()...)
		if i < len(constraints)-1 { // leave the final bool on top of the stack
			program = append(program, byte(vm.OP_VERIFY))
		}
	}
	return program
}

func (sw SignatureWitness) Materialize(tpl *Template, index int) ([][]byte, error) {
	input := tpl.Transaction.Inputs[index]
	var multiSig []byte
	if input.IsIssuance() {
		multiSig = input.IssuanceProgram()
	} else {
		multiSig = input.ControlProgram()
	}
	pubkeys, quorum, err := vmutil.ParseP2DPMultiSigProgram(multiSig)
	if err != nil {
		return nil, errors.Wrap(err, "parsing input program script")
	}
	var sigs [][]byte
	h := sha3.Sum256(sw.Program)
	for i := 0; i < len(pubkeys) && len(sigs) < quorum; i++ {
		k := indexSig(pubkeys[i], h[:], sw.Sigs)
		if k >= 0 {
			sigs = append(sigs, sw.Sigs[k])
		}
	}
	return append(sigs, sw.Program), nil
}

func indexSig(key ed25519.PublicKey, msg []byte, sigs []chainjson.HexBytes) int {
	for i, sig := range sigs {
		if ed25519.Verify(key, msg, sig) {
			return i
		}
	}
	return -1
}

func (sw SignatureWitness) MarshalJSON() ([]byte, error) {
	obj := struct {
		Type   string               `json:"type"`
		Quorum int                  `json:"quorum"`
		Keys   []KeyID              `json:"keys"`
		Sigs   []chainjson.HexBytes `json:"signatures"`
	}{
		Type:   "signature",
		Quorum: sw.Quorum,
		Keys:   sw.Keys,
		Sigs:   sw.Sigs,
	}
	return json.Marshal(obj)
}

func (si *SigningInstruction) AddWitnessKeys(keys []KeyID, quorum int) {
	sw := &SignatureWitness{
		Quorum: quorum,
		Keys:   keys,
	}
	si.WitnessComponents = append(si.WitnessComponents, sw)
}
<commit_msg>core/txbuilder: fix input selection to use position<commit_after>package txbuilder

import (
	"context"
	"encoding/json"

	"golang.org/x/crypto/sha3"

	"chain/crypto/ed25519"
	chainjson "chain/encoding/json"
	"chain/errors"
	"chain/protocol/bc"
	"chain/protocol/vm"
	"chain/protocol/vmutil"
)

// WitnessComponent encodes instructions for finalizing a transaction
// by populating its InputWitness fields. Each WitnessComponent object
// produces zero or more items for the InputWitness of the txinput it
// corresponds to.
type WitnessComponent interface {
	// Sign is called to add signatures. Actual signing is delegated to
	// a callback function.
	Sign(context.Context, *Template, int, []string, func(context.Context, string, []uint32, [32]byte) ([]byte, error)) error

	// Materialize is called to turn the component into a vector of
	// arguments for the input witness.
	Materialize(*Template, int) ([][]byte, error)
}

// materializeWitnesses takes a filled in Template and "materializes"
// each witness component, turning it into a vector of arguments for
// the tx's input witness, creating a fully-signed transaction.
func materializeWitnesses(txTemplate *Template) error {
	msg := txTemplate.Transaction

	if msg == nil {
		return errors.Wrap(ErrMissingRawTx)
	}

	if len(txTemplate.SigningInstructions) > len(msg.Inputs) {
		return errors.Wrap(ErrBadInstructionCount)
	}

	for i, sigInst := range txTemplate.SigningInstructions {
		if msg.Inputs[sigInst.Position] == nil {
			return errors.WithDetailf(ErrBadTxInputIdx, "signing instruction %d references missing tx input %d", i, sigInst.Position)
		}

		var witness [][]byte
		for j, c := range sigInst.WitnessComponents {
			items, err := c.Materialize(txTemplate, int(sigInst.Position))
			if err != nil {
				return errors.WithDetailf(err, "error in witness component %d of input %d", j, i)
			}
			witness = append(witness, items...)
		}

		msg.Inputs[sigInst.Position].InputWitness = witness
	}

	return nil
}

type (
	SignatureWitness struct {
		// Quorum is the number of signatures required.
		Quorum int `json:"quorum"`

		// Keys are the identities of the keys to sign with.
		Keys []KeyID `json:"keys"`

		// Program is the deferred predicate, whose hash is what gets
		// signed. If empty, it is computed during Sign from the outputs
		// and the current input of the transaction.
		Program chainjson.HexBytes `json:"program"`

		// Sigs are signatures of Program made from each of the Keys
		// during Sign.
		Sigs []chainjson.HexBytes `json:"signatures"`
	}

	KeyID struct {
		XPub           string   `json:"xpub"`
		DerivationPath []uint32 `json:"derivation_path"`
	}
)

var ErrEmptyProgram = errors.New("empty signature program")

// Sign populates sw.Sigs with as many signatures of the deferred predicate in
// sw.Program as it can from the set of keys in sw.Keys.
//
// If sw.Program is empty, it is populated with an _inferred_ deferred
// predicate: a program committing to aspects of the current
// transaction. Specifically, the program commits to:
//  - the mintime and maxtime of the transaction (if non-zero)
//  - the outpoint and (if non-empty) reference data of the current input
//  - the assetID, amount, control program, and (if non-empty) reference data of each output.
func (sw *SignatureWitness) Sign(ctx context.Context, tpl *Template, index int, xpubs []string, signFn func(context.Context, string, []uint32, [32]byte) ([]byte, error)) error {
	// Compute the deferred predicate to sign. This is either a
	// txsighash program if tpl.Final is true (i.e., the tx is complete
	// and no further changes are allowed) or a program enforcing
	// constraints derived from the existing outputs and current input.
	if len(sw.Program) == 0 {
		sw.Program = buildSigProgram(tpl, int(tpl.SigningInstructions[index].Position))
		if len(sw.Program) == 0 {
			return ErrEmptyProgram
		}
	}
	if len(sw.Sigs) < len(sw.Keys) {
		// Each key in sw.Keys may produce a signature in sw.Sigs. Make
		// sure there are enough slots in sw.Sigs and that we preserve any
		// sigs already present.
		newSigs := make([]chainjson.HexBytes, len(sw.Keys))
		copy(newSigs, sw.Sigs)
		sw.Sigs = newSigs
	}
	h := sha3.Sum256(sw.Program)
	for i, keyID := range sw.Keys {
		if len(sw.Sigs[i]) > 0 {
			// Already have a signature for this key
			continue
		}
		if !contains(xpubs, keyID.XPub) {
			continue
		}
		sigBytes, err := signFn(ctx, keyID.XPub, keyID.DerivationPath, h)
		if err != nil {
			return errors.WithDetailf(err, "computing signature %d", i)
		}
		sw.Sigs[i] = sigBytes
	}
	return nil
}

func contains(list []string, key string) bool {
	for _, k := range list {
		if k == key {
			return true
		}
	}
	return false
}

func buildSigProgram(tpl *Template, index int) []byte {
	if tpl.Final {
		h := tpl.Hash(index)
		builder := vmutil.NewBuilder()
		builder.AddData(h[:])
		builder.AddOp(vm.OP_TXSIGHASH).AddOp(vm.OP_EQUAL)
		return builder.Program
	}
	constraints := make([]constraint, 0, 3+len(tpl.Transaction.Outputs))
	constraints = append(constraints, &timeConstraint{
		minTimeMS: tpl.Transaction.MinTime,
		maxTimeMS: tpl.Transaction.MaxTime,
	})
	inp := tpl.Transaction.Inputs[index]
	if !inp.IsIssuance() {
		constraints = append(constraints, outpointConstraint(inp.Outpoint()))
	}
	if len(inp.ReferenceData) > 0 {
		constraints = append(constraints, refdataConstraint(inp.ReferenceData))
	}
	for _, out := range tpl.Transaction.Outputs {
		c := &payConstraint{
			AssetAmount: out.AssetAmount,
			Program:     out.ControlProgram,
		}
		if len(out.ReferenceData) > 0 {
			h := sha3.Sum256(out.ReferenceData)
			c.RefDataHash = (*bc.Hash)(&h)
		}
		constraints = append(constraints, c)
	}
	var program []byte
	for i, c := range constraints {
		program = append(program, c.code()...)
		if i < len(constraints)-1 { // leave the final bool on top of the stack
			program = append(program, byte(vm.OP_VERIFY))
		}
	}
	return program
}

func (sw SignatureWitness) Materialize(tpl *Template, index int) ([][]byte, error) {
	input := tpl.Transaction.Inputs[index]
	var multiSig []byte
	if input.IsIssuance() {
		multiSig = input.IssuanceProgram()
	} else {
		multiSig = input.ControlProgram()
	}
	pubkeys, quorum, err := vmutil.ParseP2DPMultiSigProgram(multiSig)
	if err != nil {
		return nil, errors.Wrap(err, "parsing input program script")
	}
	var sigs [][]byte
	h := sha3.Sum256(sw.Program)
	for i := 0; i < len(pubkeys) && len(sigs) < quorum; i++ {
		k := indexSig(pubkeys[i], h[:], sw.Sigs)
		if k >= 0 {
			sigs = append(sigs, sw.Sigs[k])
		}
	}
	return append(sigs, sw.Program), nil
}

func indexSig(key ed25519.PublicKey, msg []byte, sigs []chainjson.HexBytes) int {
	for i, sig := range sigs {
		if ed25519.Verify(key, msg, sig) {
			return i
		}
	}
	return -1
}

func (sw SignatureWitness) MarshalJSON() ([]byte, error) {
	obj := struct {
		Type   string               `json:"type"`
		Quorum int                  `json:"quorum"`
		Keys   []KeyID              `json:"keys"`
		Sigs   []chainjson.HexBytes `json:"signatures"`
	}{
		Type:   "signature",
		Quorum: sw.Quorum,
		Keys:   sw.Keys,
		Sigs:   sw.Sigs,
	}
	return json.Marshal(obj)
}

func (si *SigningInstruction) AddWitnessKeys(keys []KeyID, quorum int) {
	sw := &SignatureWitness{
		Quorum: quorum,
		Keys:   keys,
	}
	si.WitnessComponents = append(si.WitnessComponents, sw)
}
<|endoftext|>


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

package json

import (
	"encoding/json"
	"errors"
	"net/http"

	"github.com/segmentio/gorilla-rpc"
)

var null = json.RawMessage([]byte("null"))

// ----------------------------------------------------------------------------
// Request and Response
// ----------------------------------------------------------------------------

// serverRequest represents a JSON-RPC request received by the server.
type serverRequest struct {
	// A String containing the name of the method to be invoked.
	Method string `json:"method"`
	// An Array of objects to pass as arguments to the method.
	Params *json.RawMessage `json:"params"`
	// The request id. This can be of any type. It is used to match the
	// response with the request that it is replying to.
	Id *json.RawMessage `json:"id"`
}

// serverResponse represents a JSON-RPC response returned by the server.
type serverResponse struct {
	// The Object that was returned by the invoked method. This must be null
	// in case there was an error invoking the method.
	Result interface{} `json:"result"`
	// An Error object if there was an error invoking the method. It must be
	// null if there was no error.
	Error interface{} `json:"error"`
	// This must be the same id as the request it is responding to.
	Id *json.RawMessage `json:"id"`
}

// ----------------------------------------------------------------------------
// Codec
// ----------------------------------------------------------------------------

// NewCodec returns a new JSON Codec.
func NewCodec() *Codec {
	return &Codec{}
}

// Codec creates a CodecRequest to process each request.
type Codec struct {
}

// NewRequest returns a CodecRequest.
func (c *Codec) NewRequest(r *http.Request) rpc.CodecRequest {
	return newCodecRequest(r)
}

// ----------------------------------------------------------------------------
// CodecRequest
// ----------------------------------------------------------------------------

// newCodecRequest returns a new CodecRequest.
func newCodecRequest(r *http.Request) rpc.CodecRequest {
	// Decode the request body and check if RPC method is valid.
	req := new(serverRequest)
	err := json.NewDecoder(r.Body).Decode(req)
	r.Body.Close()
	return &CodecRequest{request: req, err: err}
}

// CodecRequest decodes and encodes a single request.
type CodecRequest struct {
	request *serverRequest
	err     error
}

// Method returns the RPC method for the current request.
//
// The method uses a dotted notation as in "Service.Method".
func (c *CodecRequest) Method() (string, error) {
	if c.err == nil {
		return c.request.Method, nil
	}
	return "", c.err
}

// ReadRequest fills the request object for the RPC method.
func (c *CodecRequest) ReadRequest(args interface{}) error {
	if c.err == nil {
		if c.request.Params != nil {
			// JSON params is array value. RPC params is struct.
			// Unmarshal into array containing the request struct.
			params := [1]interface{}{args}
			c.err = json.Unmarshal(*c.request.Params, &params)
		} else {
			c.err = errors.New("rpc: method request ill-formed: missing params field")
		}
	}
	return c.err
}

// WriteResponse encodes the response and writes it to the ResponseWriter.
//
// The err parameter is the error resulted from calling the RPC method,
// or nil if there was no error.
func (c *CodecRequest) WriteResponse(w http.ResponseWriter, reply interface{}, methodErr error) error {
	if c.err != nil {
		return c.err
	}
	res := &serverResponse{
		Result: reply,
		Error:  &null,
		Id:     c.request.Id,
	}
	if methodErr != nil {
		// Propagate error message as string.
		res.Error = methodErr.Error()
		// Result must be null if there was an error invoking the method.
		// http://json-rpc.org/wiki/specification#a1.2Response
		res.Result = &null
	}
	if c.request.Id == nil {
		// Id is null for notifications and they don't have a response.
		res.Id = &null
	} else {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		encoder := json.NewEncoder(w)
		encoder.Encode(res)
	}
	return nil
}
<commit_msg>Fix import<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package json

import (
	"encoding/json"
	"errors"
	"net/http"

	"github.com/gorilla/rpc"
)

var null = json.RawMessage([]byte("null"))

// ----------------------------------------------------------------------------
// Request and Response
// ----------------------------------------------------------------------------

// serverRequest represents a JSON-RPC request received by the server.
type serverRequest struct {
	// A String containing the name of the method to be invoked.
	Method string `json:"method"`
	// An Array of objects to pass as arguments to the method.
	Params *json.RawMessage `json:"params"`
	// The request id. This can be of any type. It is used to match the
	// response with the request that it is replying to.
	Id *json.RawMessage `json:"id"`
}

// serverResponse represents a JSON-RPC response returned by the server.
type serverResponse struct {
	// The Object that was returned by the invoked method. This must be null
	// in case there was an error invoking the method.
	Result interface{} `json:"result"`
	// An Error object if there was an error invoking the method. It must be
	// null if there was no error.
	Error interface{} `json:"error"`
	// This must be the same id as the request it is responding to.
	Id *json.RawMessage `json:"id"`
}

// ----------------------------------------------------------------------------
// Codec
// ----------------------------------------------------------------------------

// NewCodec returns a new JSON Codec.
func NewCodec() *Codec {
	return &Codec{}
}

// Codec creates a CodecRequest to process each request.
type Codec struct {
}

// NewRequest returns a CodecRequest.
func (c *Codec) NewRequest(r *http.Request) rpc.CodecRequest {
	return newCodecRequest(r)
}

// ----------------------------------------------------------------------------
// CodecRequest
// ----------------------------------------------------------------------------

// newCodecRequest returns a new CodecRequest.
func newCodecRequest(r *http.Request) rpc.CodecRequest {
	// Decode the request body and check if RPC method is valid.
	req := new(serverRequest)
	err := json.NewDecoder(r.Body).Decode(req)
	r.Body.Close()
	return &CodecRequest{request: req, err: err}
}

// CodecRequest decodes and encodes a single request.
type CodecRequest struct {
	request *serverRequest
	err     error
}

// Method returns the RPC method for the current request.
//
// The method uses a dotted notation as in "Service.Method".
func (c *CodecRequest) Method() (string, error) {
	if c.err == nil {
		return c.request.Method, nil
	}
	return "", c.err
}

// ReadRequest fills the request object for the RPC method.
func (c *CodecRequest) ReadRequest(args interface{}) error {
	if c.err == nil {
		if c.request.Params != nil {
			// JSON params is array value. RPC params is struct.
			// Unmarshal into array containing the request struct.
			params := [1]interface{}{args}
			c.err = json.Unmarshal(*c.request.Params, &params)
		} else {
			c.err = errors.New("rpc: method request ill-formed: missing params field")
		}
	}
	return c.err
}

// WriteResponse encodes the response and writes it to the ResponseWriter.
//
// The err parameter is the error resulted from calling the RPC method,
// or nil if there was no error.
func (c *CodecRequest) WriteResponse(w http.ResponseWriter, reply interface{}, methodErr error) error {
	if c.err != nil {
		return c.err
	}
	res := &serverResponse{
		Result: reply,
		Error:  &null,
		Id:     c.request.Id,
	}
	if methodErr != nil {
		// Propagate error message as string.
		res.Error = methodErr.Error()
		// Result must be null if there was an error invoking the method.
		// http://json-rpc.org/wiki/specification#a1.2Response
		res.Result = &null
	}
	if c.request.Id == nil {
		// Id is null for notifications and they don't have a response.
		res.Id = &null
	} else {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		encoder := json.NewEncoder(w)
		encoder.Encode(res)
	}
	return nil
}
<|endoftext|>


<commit_before>package migrations

import (
	"context"
	"path"
	"restic"
	"restic/backend"
	"restic/backend/s3"
	"restic/debug"
	"restic/errors"
)

func init() {
	register(&S3Layout{})
}

// S3Layout migrates a repository on an S3 backend from the "s3legacy" to the
// "default" layout.
type S3Layout struct{}

// Check tests whether the migration can be applied.
func (m *S3Layout) Check(ctx context.Context, repo restic.Repository) (bool, error) {
	be, ok := repo.Backend().(*s3.Backend)
	if !ok {
		debug.Log("backend is not s3")
		return false, nil
	}

	if be.Layout.Name() != "s3legacy" {
		debug.Log("layout is not s3legacy")
		return false, nil
	}

	return true, nil
}

func (m *S3Layout) moveFiles(ctx context.Context, be *s3.Backend, l backend.Layout, t restic.FileType) error {
	for name := range be.List(ctx, t) {
		h := restic.Handle{Type: t, Name: name}
		debug.Log("move %v", h)
		if err := be.Rename(h, l); err != nil {
			return err
		}
	}

	return nil
}

// Apply runs the migration.
func (m *S3Layout) Apply(ctx context.Context, repo restic.Repository) error {
	be, ok := repo.Backend().(*s3.Backend)
	if !ok {
		debug.Log("backend is not s3")
		return errors.New("backend is not s3")
	}

	oldLayout := &backend.S3LegacyLayout{
		Path: be.Path(),
		Join: path.Join,
	}

	newLayout := &backend.DefaultLayout{
		Path: be.Path(),
		Join: path.Join,
	}

	be.Layout = oldLayout

	for _, t := range []restic.FileType{
		restic.SnapshotFile,
		restic.DataFile,
		restic.KeyFile,
		restic.LockFile,
	} {
		err := m.moveFiles(ctx, be, newLayout, t)
		if err != nil {
			return err
		}
	}

	be.Layout = newLayout

	return nil
}

// Name returns the name for this migration.
func (m *S3Layout) Name() string {
	return "s3_layout"
}

// Desc returns a short description what the migration does.
func (m *S3Layout) Desc() string {
	return "move files from 's3legacy' to the 'default' repository layout"
}
<commit_msg>s3 migrate layout: Retry on errors<commit_after>package migrations

import (
	"context"
	"fmt"
	"os"
	"path"
	"restic"
	"restic/backend"
	"restic/backend/s3"
	"restic/debug"
	"restic/errors"
)

func init() {
	register(&S3Layout{})
}

// S3Layout migrates a repository on an S3 backend from the "s3legacy" to the
// "default" layout.
type S3Layout struct{}

// Check tests whether the migration can be applied.
func (m *S3Layout) Check(ctx context.Context, repo restic.Repository) (bool, error) {
	be, ok := repo.Backend().(*s3.Backend)
	if !ok {
		debug.Log("backend is not s3")
		return false, nil
	}

	if be.Layout.Name() != "s3legacy" {
		debug.Log("layout is not s3legacy")
		return false, nil
	}

	return true, nil
}

func retry(max int, fail func(err error), f func() error) error {
	var err error
	for i := 0; i < max; i++ {
		err = f()
		if err == nil {
			return err
		}
		if fail != nil {
			fail(err)
		}
	}
	return err
}

// maxErrors for retrying renames on s3.
const maxErrors = 20

func (m *S3Layout) moveFiles(ctx context.Context, be *s3.Backend, l backend.Layout, t restic.FileType) error {
	printErr := func(err error) {
		fmt.Fprintf(os.Stderr, "renaming file returned error: %v\n", err)
	}

	for name := range be.List(ctx, t) {
		h := restic.Handle{Type: t, Name: name}
		debug.Log("move %v", h)

		retry(maxErrors, printErr, func() error {
			return be.Rename(h, l)
		})
	}

	return nil
}

// Apply runs the migration.
func (m *S3Layout) Apply(ctx context.Context, repo restic.Repository) error {
	be, ok := repo.Backend().(*s3.Backend)
	if !ok {
		debug.Log("backend is not s3")
		return errors.New("backend is not s3")
	}

	oldLayout := &backend.S3LegacyLayout{
		Path: be.Path(),
		Join: path.Join,
	}

	newLayout := &backend.DefaultLayout{
		Path: be.Path(),
		Join: path.Join,
	}

	be.Layout = oldLayout

	for _, t := range []restic.FileType{
		restic.SnapshotFile,
		restic.DataFile,
		restic.KeyFile,
		restic.LockFile,
	} {
		err := m.moveFiles(ctx, be, newLayout, t)
		if err != nil {
			return err
		}
	}

	be.Layout = newLayout

	return nil
}

// Name returns the name for this migration.
func (m *S3Layout) Name() string {
	return "s3_layout"
}

// Desc returns a short description what the migration does.
func (m *S3Layout) Desc() string {
	return "move files from 's3legacy' to the 'default' repository layout"
}
<|endoftext|>


<commit_before>package strongmessage

import (
  "fmt"
  zmq "github.com/alecthomas/gozmq"
  "strongmessage/config"
	"strongmessage/objects"
)

func BootstrapNetwork(log chan string, message_channel chan objects.Message) {
	peers := config.LoadPeers(log)
  log <- fmt.Sprintf("%v", peers)
  if peers == nil {
		log <- "Failed to load peers.json"
	} else {
		context, err := zmq.NewContext()
		if err != nil {
			log <- "Error creating ZMQ context"
			log <- err.Error()
		} else {
			for _, v := range peers {
				go v.Subscribe(log, message_channel, context)
			}
		}
	}
}

func StartPubServer(log chan string, message_channel chan objects.Message) error {
	context, err := zmq.NewContext()
	if err != nil {
		log <- "Error creating ZMQ context"
		log <- err.Error()
    return err
	} else {
		socket, err := context.NewSocket(zmq.PUB)
		if err != nil {
			log <- "Error creating socket."
			log <- err.Error()
		}
    tcpString := fmt.Sprintf("tcp://%s:%d", config.DOMAIN, config.PORT)
		socket.Bind(tcpString)
		for {
			message := <- message_channel
			bytes := message.GetBytes(log)
			socket.Send(bytes, 0)
		}
    return nil
	}
}

func BlockingLogger(channel chan string) {
	for {
		log_message := <-channel
		fmt.Println(log_message)
	}
}
<commit_msg>renamed strongmessage.go<commit_after>package strongmessage

import (
  "fmt"
  zmq "github.com/alecthomas/gozmq"
  "strongmessage/config"
  "strongmessage/objects"
)

func BootstrapNetwork(log chan string, message_channel chan objects.Message) {
  peers := config.LoadPeers(log)
  log <- fmt.Sprintf("%v", peers)
  if peers == nil {
    log <- "Failed to load peers.json"
  } else {
    context, err := zmq.NewContext()
    if err != nil {
      log <- "Error creating ZMQ context"
      log <- err.Error()
    } else {
      for _, v := range peers {
        go v.Subscribe(log, message_channel, context)
      }
    }
  }
}

func StartPubServer(log chan string, message_channel chan objects.Message) error {
  context, err := zmq.NewContext()
  if err != nil {
    log <- "Error creating ZMQ context"
    log <- err.Error()
    return err
  } else {
    socket, err := context.NewSocket(zmq.PUB)
    if err != nil {
      log <- "Error creating socket."
      log <- err.Error()
    }
    tcpString := fmt.Sprintf("tcp://%s:%d", config.DOMAIN, config.PORT)
    socket.Bind(tcpString)
    for {
      message := <- message_channel
      bytes := message.GetBytes(log)
      socket.Send(bytes, 0)
    }
    return nil
  }
}

func BlockingLogger(channel chan string) {
  for {
    log_message := <-channel
    fmt.Println(log_message)
  }
}
<|endoftext|>