<commit_before>package dhcpalloc

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"math"
	"math/big"
	"net"
	"os"

	"github.com/mdlayher/netx/eui64"

	"github.com/lxc/lxd/lxd/dnsmasq"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/logger"
)

// ErrDHCPNotSupported indicates network doesn't support DHCP for this IP protocol.
var ErrDHCPNotSupported error = errors.New("Network doesn't support DHCP")

// DHCPValidIP returns whether an IP fits inside one of the supplied DHCP ranges and subnet.
func DHCPValidIP(subnet *net.IPNet, ranges []shared.IPRange, IP net.IP) bool {
	inSubnet := subnet.Contains(IP)
	if !inSubnet {
		return false
	}

	if len(ranges) > 0 {
		for _, IPRange := range ranges {
			if bytes.Compare(IP, IPRange.Start) >= 0 && bytes.Compare(IP, IPRange.End) <= 0 {
				return true
			}
		}
	} else if inSubnet {
		return true
	}

	return false
}

// GetIP returns a net.IP representing the IP belonging to the subnet for the host number supplied.
func GetIP(subnet *net.IPNet, host int64) net.IP {
	// Convert IP to a big int.
	bigIP := big.NewInt(0)
	bigIP.SetBytes(subnet.IP.To16())

	// Deal with negative offsets.
	bigHost := big.NewInt(host)
	bigCount := big.NewInt(host)
	if host < 0 {
		mask, size := subnet.Mask.Size()

		bigHosts := big.NewFloat(0)
		bigHosts.SetFloat64((math.Pow(2, float64(size-mask))))
		bigHostsInt, _ := bigHosts.Int(nil)

		bigCount.Set(bigHostsInt)
		bigCount.Add(bigCount, bigHost)
	}

	// Get the new IP int.
	bigIP.Add(bigIP, bigCount)

	// Generate an IPv6.
	if subnet.IP.To4() == nil {
		newIP := bigIP.Bytes()
		return newIP
	}

	// Generate an IPv4.
	newIP := make(net.IP, 4)
	binary.BigEndian.PutUint32(newIP, uint32(bigIP.Int64()))
	return newIP
}

// Network represents a LXD network responsible for running dnsmasq.
type Network interface {
	Name() string
	Type() string
	Config() map[string]string
	DHCPv4Subnet() *net.IPNet
	DHCPv6Subnet() *net.IPNet
	DHCPv4Ranges() []shared.IPRange
	DHCPv6Ranges() []shared.IPRange
}

// Options to initialise the allocator with.
type Options struct {
	ProjectName string
	HostName    string
	DeviceName  string
	HostMAC     net.HardwareAddr
	Network     Network
}

// Transaction is a locked transaction of the dnsmasq config files that allows IP allocations for a host.
type Transaction struct {
	opts              *Options
	currentDHCPMAC    net.HardwareAddr
	currentDHCPv4     dnsmasq.DHCPAllocation
	currentDHCPv6     dnsmasq.DHCPAllocation
	allocationsDHCPv4 map[[4]byte]dnsmasq.DHCPAllocation
	allocationsDHCPv6 map[[16]byte]dnsmasq.DHCPAllocation
	allocatedIPv4     net.IP
	allocatedIPv6     net.IP
}

// AllocateIPv4 allocate an IPv4 static DHCP allocation.
func (t *Transaction) AllocateIPv4() (net.IP, error) {
	var err error

	// Should have a (at least empty) map if DHCP is supported.
	if t.allocationsDHCPv4 == nil {
		return nil, ErrDHCPNotSupported
	}

	dhcpSubnet := t.opts.Network.DHCPv4Subnet()
	if dhcpSubnet == nil {
		return nil, ErrDHCPNotSupported
	}

	// Check the existing allocated IP is still valid in the network's subnet & ranges, if not then
	// we'll need to generate a new one.
	if t.allocatedIPv4 != nil {
		ranges := t.opts.Network.DHCPv4Ranges()
		if !DHCPValidIP(dhcpSubnet, ranges, t.allocatedIPv4.To4()) {
			t.allocatedIPv4 = nil // We need a new IP allocated.
		}
	}

	// Allocate a new IPv4 address if needed.
	if t.allocatedIPv4 == nil {
		t.allocatedIPv4, err = t.getDHCPFreeIPv4(t.allocationsDHCPv4, t.opts.HostName, t.opts.HostMAC)
		if err != nil {
			return nil, err
		}
	}

	return t.allocatedIPv4, nil
}

// AllocateIPv6 allocate an IPv6 static DHCP allocation.
func (t *Transaction) AllocateIPv6() (net.IP, error) {
	var err error

	// Should have a (at least empty) map if DHCP is supported.
	if t.allocationsDHCPv6 == nil {
		return nil, ErrDHCPNotSupported
	}

	dhcpSubnet := t.opts.Network.DHCPv6Subnet()
	if dhcpSubnet == nil {
		return nil, ErrDHCPNotSupported
	}

	// Check the existing allocated IP is still valid in the network's subnet & ranges, if not then
	// we'll need to generate a new one.
	if t.allocatedIPv6 != nil {
		ranges := t.opts.Network.DHCPv6Ranges()
		if !DHCPValidIP(dhcpSubnet, ranges, t.allocatedIPv6.To16()) {
			t.allocatedIPv6 = nil // We need a new IP allocated.
		}
	}

	// Allocate a new IPv6 address if needed.
	if t.allocatedIPv6 == nil {
		t.allocatedIPv6, err = t.getDHCPFreeIPv6(t.allocationsDHCPv6, t.opts.HostName, t.opts.HostMAC)
		if err != nil {
			return nil, err
		}
	}

	return t.allocatedIPv6, nil
}

// getDHCPFreeIPv4 attempts to find a free IPv4 address for the device.
// It first checks whether there is an existing allocation for the instance.
// If no previous allocation, then a free IP is picked from the ranges configured.
func (t *Transaction) getDHCPFreeIPv4(usedIPs map[[4]byte]dnsmasq.DHCPAllocation, deviceStaticFileName string, mac net.HardwareAddr) (net.IP, error) {
	lxdIP, subnet, err := net.ParseCIDR(t.opts.Network.Config()["ipv4.address"])
	if err != nil {
		return nil, err
	}

	dhcpRanges := t.opts.Network.DHCPv4Ranges()

	// Lets see if there is already an allocation for our device and that it sits within subnet.
	// If there are custom DHCP ranges defined, check also that the IP falls within one of the ranges.
	for _, DHCP := range usedIPs {
		if (deviceStaticFileName == DHCP.StaticFileName || bytes.Equal(mac, DHCP.MAC)) && DHCPValidIP(subnet, dhcpRanges, DHCP.IP) {
			return DHCP.IP, nil
		}
	}

	// If no custom ranges defined, convert subnet pool to a range.
	if len(dhcpRanges) <= 0 {
		dhcpRanges = append(dhcpRanges, shared.IPRange{
			Start: GetIP(subnet, 1).To4(),
			End:   GetIP(subnet, -2).To4()},
		)
	}

	// If no valid existing allocation found, try and find a free one in the subnet pool/ranges.
	for _, IPRange := range dhcpRanges {
		inc := big.NewInt(1)
		startBig := big.NewInt(0)
		startBig.SetBytes(IPRange.Start)
		endBig := big.NewInt(0)
		endBig.SetBytes(IPRange.End)

		for {
			if startBig.Cmp(endBig) >= 0 {
				break
			}

			IP := net.IP(startBig.Bytes())

			// Check IP generated is not LXD's IP.
			if IP.Equal(lxdIP) {
				startBig.Add(startBig, inc)
				continue
			}

			// Check IP is not already allocated.
			var IPKey [4]byte
			copy(IPKey[:], IP.To4())

			_, inUse := usedIPs[IPKey]
			if inUse {
				startBig.Add(startBig, inc)
				continue
			}

			return IP, nil
		}
	}

	return nil, fmt.Errorf("No available IP could not be found")
}

// getDHCPFreeIPv6 attempts to find a free IPv6 address for the device.
// It first checks whether there is an existing allocation for the instance. Due to the limitations
// of dnsmasq lease file format, we can only search for previous static allocations.
// If no previous allocation, then if SLAAC (stateless) mode is enabled on the network, or if
// DHCPv6 stateful mode is enabled without custom ranges, then an EUI64 IP is generated from the
// device's MAC address. Finally if stateful custom ranges are enabled, then a free IP is picked
// from the ranges configured.
func (t *Transaction) getDHCPFreeIPv6(usedIPs map[[16]byte]dnsmasq.DHCPAllocation, deviceStaticFileName string, mac net.HardwareAddr) (net.IP, error) {
	lxdIP, subnet, err := net.ParseCIDR(t.opts.Network.Config()["ipv6.address"])
	if err != nil {
		return nil, err
	}

	dhcpRanges := t.opts.Network.DHCPv6Ranges()

	// Lets see if there is already an allocation for our device and that it sits within subnet.
	// Because of dnsmasq's lease file format we can only match safely against static
	// allocations using instance name. If there are custom DHCP ranges defined, check also
	// that the IP falls within one of the ranges.
	for _, DHCP := range usedIPs {
		if deviceStaticFileName == DHCP.StaticFileName && DHCPValidIP(subnet, dhcpRanges, DHCP.IP) {
			return DHCP.IP, nil
		}
	}

	netConfig := t.opts.Network.Config()

	// Try using an EUI64 IP when in either SLAAC or DHCPv6 stateful mode without custom ranges.
	if shared.IsFalseOrEmpty(netConfig["ipv6.dhcp.stateful"]) || netConfig["ipv6.dhcp.ranges"] == "" {
		IP, err := eui64.ParseMAC(subnet.IP, mac)
		if err != nil {
			return nil, err
		}

		// Check IP is not already allocated and not the LXD IP.
		var IPKey [16]byte
		copy(IPKey[:], IP.To16())
		_, inUse := usedIPs[IPKey]
		if !inUse && !IP.Equal(lxdIP) {
			return IP, nil
		}
	}

	// If no custom ranges defined, convert subnet pool to a range.
	if len(dhcpRanges) <= 0 {
		dhcpRanges = append(dhcpRanges, shared.IPRange{
			Start: GetIP(subnet, 1).To16(),
			End:   GetIP(subnet, -1).To16()},
		)
	}

	// If we get here, then someone already has our SLAAC IP, or we are using custom ranges.
	// Try and find a free one in the subnet pool/ranges.
	for _, IPRange := range dhcpRanges {
		inc := big.NewInt(1)
		startBig := big.NewInt(0)
		startBig.SetBytes(IPRange.Start)
		endBig := big.NewInt(0)
		endBig.SetBytes(IPRange.End)

		for {
			if startBig.Cmp(endBig) >= 0 {
				break
			}

			IP := net.IP(startBig.Bytes())

			// Check IP generated is not LXD's IP.
			if IP.Equal(lxdIP) {
				startBig.Add(startBig, inc)
				continue
			}

			// Check IP is not already allocated.
			var IPKey [16]byte
			copy(IPKey[:], IP.To16())

			_, inUse := usedIPs[IPKey]
			if inUse {
				startBig.Add(startBig, inc)
				continue
			}

			return IP, nil
		}
	}

	return nil, fmt.Errorf("No available IP could not be found")
}

// AllocateTask initialises a new locked Transaction for a specific host and executes the supplied function on it.
// The lock on the dnsmasq config is released when the function returns.
func AllocateTask(opts *Options, f func(*Transaction) error) error {
	l := logger.AddContext(logger.Log, logger.Ctx{"driver": opts.Network.Type(), "network": opts.Network.Name(), "project": opts.ProjectName, "host": opts.HostName})

	dnsmasq.ConfigMutex.Lock()
	defer dnsmasq.ConfigMutex.Unlock()

	var err error
	t := &Transaction{opts: opts}

	// Read current static IP allocation configured from dnsmasq host config (if exists).
	deviceStaticFileName := dnsmasq.StaticAllocationFileName(opts.ProjectName, opts.HostName, opts.DeviceName)
	t.currentDHCPMAC, t.currentDHCPv4, t.currentDHCPv6, err = dnsmasq.DHCPStaticAllocation(opts.Network.Name(), deviceStaticFileName)
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	// Set the current allocated IPs internally (these may be changed later), and if they are it will trigger
	// a dnsmasq static host config file rebuild.
	t.allocatedIPv4 = t.currentDHCPv4.IP
	t.allocatedIPv6 = t.currentDHCPv6.IP

	// Get all existing allocations in network if leases file exists. If not then we will detect this later
	// due to the existing allocations maps being nil.
	if shared.PathExists(shared.VarPath("networks", opts.Network.Name(), "dnsmasq.leases")) {
		t.allocationsDHCPv4, t.allocationsDHCPv6, err = dnsmasq.DHCPAllAllocations(opts.Network.Name())
		if err != nil {
			return err
		}
	}

	// Run the supplied allocation function.
	err = f(t)
	if err != nil {
		return err
	}

	// If the user's function didn't fail despite DHCP allocation not being available for either protocol,
	// then presumably they ignored allocation errors. So fail here, rather than try to write the new config
	// out, which will fail with a less helpful error about missing paths.
	if t.allocationsDHCPv4 == nil && t.allocationsDHCPv6 == nil {
		return ErrDHCPNotSupported
	}

	// If MAC or either IPv4 or IPv6 assigned is different than what is in dnsmasq config, rebuild config.
	macChanged := !bytes.Equal(opts.HostMAC, t.currentDHCPMAC)
	ipv4Changed := (t.allocatedIPv4 != nil && !bytes.Equal(t.currentDHCPv4.IP, t.allocatedIPv4.To4()))
	ipv6Changed := (t.allocatedIPv6 != nil && !bytes.Equal(t.currentDHCPv6.IP, t.allocatedIPv6.To16()))

	if macChanged || ipv4Changed || ipv6Changed {
		var IPv4Str, IPv6Str string

		if t.allocatedIPv4 != nil {
			IPv4Str = t.allocatedIPv4.String()
		}

		if t.allocatedIPv6 != nil {
			IPv6Str = t.allocatedIPv6.String()
		}

		// Write out new dnsmasq static host allocation config file.
		err = dnsmasq.UpdateStaticEntry(opts.Network.Name(), opts.ProjectName, opts.HostName, opts.DeviceName, opts.Network.Config(), opts.HostMAC.String(), IPv4Str, IPv6Str)
		if err != nil {
			return err
		}
		l.Debug("Updated static DHCP entry", logger.Ctx{"mac": opts.HostMAC.String(), "IPv4": IPv4Str, "IPv6": IPv6Str})

		// Reload dnsmasq.
		err = dnsmasq.Kill(opts.Network.Name(), true)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>lxd/dnsmasq/dhcpalloc: Prefer net.IP.Equal over bytes.Equal.<commit_after>package dhcpalloc

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"math"
	"math/big"
	"net"
	"os"

	"github.com/mdlayher/netx/eui64"

	"github.com/lxc/lxd/lxd/dnsmasq"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/logger"
)

// ErrDHCPNotSupported indicates network doesn't support DHCP for this IP protocol.
var ErrDHCPNotSupported error = errors.New("Network doesn't support DHCP")

// DHCPValidIP returns whether an IP fits inside one of the supplied DHCP ranges and subnet.
func DHCPValidIP(subnet *net.IPNet, ranges []shared.IPRange, IP net.IP) bool {
	inSubnet := subnet.Contains(IP)
	if !inSubnet {
		return false
	}

	if len(ranges) > 0 {
		for _, IPRange := range ranges {
			if bytes.Compare(IP, IPRange.Start) >= 0 && bytes.Compare(IP, IPRange.End) <= 0 {
				return true
			}
		}
	} else if inSubnet {
		return true
	}

	return false
}

// GetIP returns a net.IP representing the IP belonging to the subnet for the host number supplied.
func GetIP(subnet *net.IPNet, host int64) net.IP {
	// Convert IP to a big int.
	bigIP := big.NewInt(0)
	bigIP.SetBytes(subnet.IP.To16())

	// Deal with negative offsets.
	bigHost := big.NewInt(host)
	bigCount := big.NewInt(host)
	if host < 0 {
		mask, size := subnet.Mask.Size()

		bigHosts := big.NewFloat(0)
		bigHosts.SetFloat64((math.Pow(2, float64(size-mask))))
		bigHostsInt, _ := bigHosts.Int(nil)

		bigCount.Set(bigHostsInt)
		bigCount.Add(bigCount, bigHost)
	}

	// Get the new IP int.
	bigIP.Add(bigIP, bigCount)

	// Generate an IPv6.
	if subnet.IP.To4() == nil {
		newIP := bigIP.Bytes()
		return newIP
	}

	// Generate an IPv4.
	newIP := make(net.IP, 4)
	binary.BigEndian.PutUint32(newIP, uint32(bigIP.Int64()))
	return newIP
}

// Network represents a LXD network responsible for running dnsmasq.
type Network interface {
	Name() string
	Type() string
	Config() map[string]string
	DHCPv4Subnet() *net.IPNet
	DHCPv6Subnet() *net.IPNet
	DHCPv4Ranges() []shared.IPRange
	DHCPv6Ranges() []shared.IPRange
}

// Options to initialise the allocator with.
type Options struct {
	ProjectName string
	HostName    string
	DeviceName  string
	HostMAC     net.HardwareAddr
	Network     Network
}

// Transaction is a locked transaction of the dnsmasq config files that allows IP allocations for a host.
type Transaction struct {
	opts              *Options
	currentDHCPMAC    net.HardwareAddr
	currentDHCPv4     dnsmasq.DHCPAllocation
	currentDHCPv6     dnsmasq.DHCPAllocation
	allocationsDHCPv4 map[[4]byte]dnsmasq.DHCPAllocation
	allocationsDHCPv6 map[[16]byte]dnsmasq.DHCPAllocation
	allocatedIPv4     net.IP
	allocatedIPv6     net.IP
}

// AllocateIPv4 allocate an IPv4 static DHCP allocation.
func (t *Transaction) AllocateIPv4() (net.IP, error) {
	var err error

	// Should have a (at least empty) map if DHCP is supported.
	if t.allocationsDHCPv4 == nil {
		return nil, ErrDHCPNotSupported
	}

	dhcpSubnet := t.opts.Network.DHCPv4Subnet()
	if dhcpSubnet == nil {
		return nil, ErrDHCPNotSupported
	}

	// Check the existing allocated IP is still valid in the network's subnet & ranges, if not then
	// we'll need to generate a new one.
	if t.allocatedIPv4 != nil {
		ranges := t.opts.Network.DHCPv4Ranges()
		if !DHCPValidIP(dhcpSubnet, ranges, t.allocatedIPv4.To4()) {
			t.allocatedIPv4 = nil // We need a new IP allocated.
		}
	}

	// Allocate a new IPv4 address if needed.
	if t.allocatedIPv4 == nil {
		t.allocatedIPv4, err = t.getDHCPFreeIPv4(t.allocationsDHCPv4, t.opts.HostName, t.opts.HostMAC)
		if err != nil {
			return nil, err
		}
	}

	return t.allocatedIPv4, nil
}

// AllocateIPv6 allocate an IPv6 static DHCP allocation.
func (t *Transaction) AllocateIPv6() (net.IP, error) {
	var err error

	// Should have a (at least empty) map if DHCP is supported.
	if t.allocationsDHCPv6 == nil {
		return nil, ErrDHCPNotSupported
	}

	dhcpSubnet := t.opts.Network.DHCPv6Subnet()
	if dhcpSubnet == nil {
		return nil, ErrDHCPNotSupported
	}

	// Check the existing allocated IP is still valid in the network's subnet & ranges, if not then
	// we'll need to generate a new one.
	if t.allocatedIPv6 != nil {
		ranges := t.opts.Network.DHCPv6Ranges()
		if !DHCPValidIP(dhcpSubnet, ranges, t.allocatedIPv6.To16()) {
			t.allocatedIPv6 = nil // We need a new IP allocated.
		}
	}

	// Allocate a new IPv6 address if needed.
	if t.allocatedIPv6 == nil {
		t.allocatedIPv6, err = t.getDHCPFreeIPv6(t.allocationsDHCPv6, t.opts.HostName, t.opts.HostMAC)
		if err != nil {
			return nil, err
		}
	}

	return t.allocatedIPv6, nil
}

// getDHCPFreeIPv4 attempts to find a free IPv4 address for the device.
// It first checks whether there is an existing allocation for the instance.
// If no previous allocation, then a free IP is picked from the ranges configured.
func (t *Transaction) getDHCPFreeIPv4(usedIPs map[[4]byte]dnsmasq.DHCPAllocation, deviceStaticFileName string, mac net.HardwareAddr) (net.IP, error) {
	lxdIP, subnet, err := net.ParseCIDR(t.opts.Network.Config()["ipv4.address"])
	if err != nil {
		return nil, err
	}

	dhcpRanges := t.opts.Network.DHCPv4Ranges()

	// Lets see if there is already an allocation for our device and that it sits within subnet.
	// If there are custom DHCP ranges defined, check also that the IP falls within one of the ranges.
	for _, DHCP := range usedIPs {
		if (deviceStaticFileName == DHCP.StaticFileName || bytes.Equal(mac, DHCP.MAC)) && DHCPValidIP(subnet, dhcpRanges, DHCP.IP) {
			return DHCP.IP, nil
		}
	}

	// If no custom ranges defined, convert subnet pool to a range.
	if len(dhcpRanges) <= 0 {
		dhcpRanges = append(dhcpRanges, shared.IPRange{
			Start: GetIP(subnet, 1).To4(),
			End:   GetIP(subnet, -2).To4()},
		)
	}

	// If no valid existing allocation found, try and find a free one in the subnet pool/ranges.
	for _, IPRange := range dhcpRanges {
		inc := big.NewInt(1)
		startBig := big.NewInt(0)
		startBig.SetBytes(IPRange.Start)
		endBig := big.NewInt(0)
		endBig.SetBytes(IPRange.End)

		for {
			if startBig.Cmp(endBig) >= 0 {
				break
			}

			IP := net.IP(startBig.Bytes())

			// Check IP generated is not LXD's IP.
			if IP.Equal(lxdIP) {
				startBig.Add(startBig, inc)
				continue
			}

			// Check IP is not already allocated.
			var IPKey [4]byte
			copy(IPKey[:], IP.To4())

			_, inUse := usedIPs[IPKey]
			if inUse {
				startBig.Add(startBig, inc)
				continue
			}

			return IP, nil
		}
	}

	return nil, fmt.Errorf("No available IP could not be found")
}

// getDHCPFreeIPv6 attempts to find a free IPv6 address for the device.
// It first checks whether there is an existing allocation for the instance. Due to the limitations
// of dnsmasq lease file format, we can only search for previous static allocations.
// If no previous allocation, then if SLAAC (stateless) mode is enabled on the network, or if
// DHCPv6 stateful mode is enabled without custom ranges, then an EUI64 IP is generated from the
// device's MAC address. Finally if stateful custom ranges are enabled, then a free IP is picked
// from the ranges configured.
func (t *Transaction) getDHCPFreeIPv6(usedIPs map[[16]byte]dnsmasq.DHCPAllocation, deviceStaticFileName string, mac net.HardwareAddr) (net.IP, error) {
	lxdIP, subnet, err := net.ParseCIDR(t.opts.Network.Config()["ipv6.address"])
	if err != nil {
		return nil, err
	}

	dhcpRanges := t.opts.Network.DHCPv6Ranges()

	// Lets see if there is already an allocation for our device and that it sits within subnet.
	// Because of dnsmasq's lease file format we can only match safely against static
	// allocations using instance name. If there are custom DHCP ranges defined, check also
	// that the IP falls within one of the ranges.
	for _, DHCP := range usedIPs {
		if deviceStaticFileName == DHCP.StaticFileName && DHCPValidIP(subnet, dhcpRanges, DHCP.IP) {
			return DHCP.IP, nil
		}
	}

	netConfig := t.opts.Network.Config()

	// Try using an EUI64 IP when in either SLAAC or DHCPv6 stateful mode without custom ranges.
	if shared.IsFalseOrEmpty(netConfig["ipv6.dhcp.stateful"]) || netConfig["ipv6.dhcp.ranges"] == "" {
		IP, err := eui64.ParseMAC(subnet.IP, mac)
		if err != nil {
			return nil, err
		}

		// Check IP is not already allocated and not the LXD IP.
		var IPKey [16]byte
		copy(IPKey[:], IP.To16())
		_, inUse := usedIPs[IPKey]
		if !inUse && !IP.Equal(lxdIP) {
			return IP, nil
		}
	}

	// If no custom ranges defined, convert subnet pool to a range.
	if len(dhcpRanges) <= 0 {
		dhcpRanges = append(dhcpRanges, shared.IPRange{
			Start: GetIP(subnet, 1).To16(),
			End:   GetIP(subnet, -1).To16()},
		)
	}

	// If we get here, then someone already has our SLAAC IP, or we are using custom ranges.
	// Try and find a free one in the subnet pool/ranges.
	for _, IPRange := range dhcpRanges {
		inc := big.NewInt(1)
		startBig := big.NewInt(0)
		startBig.SetBytes(IPRange.Start)
		endBig := big.NewInt(0)
		endBig.SetBytes(IPRange.End)

		for {
			if startBig.Cmp(endBig) >= 0 {
				break
			}

			IP := net.IP(startBig.Bytes())

			// Check IP generated is not LXD's IP.
			if IP.Equal(lxdIP) {
				startBig.Add(startBig, inc)
				continue
			}

			// Check IP is not already allocated.
			var IPKey [16]byte
			copy(IPKey[:], IP.To16())

			_, inUse := usedIPs[IPKey]
			if inUse {
				startBig.Add(startBig, inc)
				continue
			}

			return IP, nil
		}
	}

	return nil, fmt.Errorf("No available IP could not be found")
}

// AllocateTask initialises a new locked Transaction for a specific host and executes the supplied function on it.
// The lock on the dnsmasq config is released when the function returns.
func AllocateTask(opts *Options, f func(*Transaction) error) error {
	l := logger.AddContext(logger.Log, logger.Ctx{"driver": opts.Network.Type(), "network": opts.Network.Name(), "project": opts.ProjectName, "host": opts.HostName})

	dnsmasq.ConfigMutex.Lock()
	defer dnsmasq.ConfigMutex.Unlock()

	var err error
	t := &Transaction{opts: opts}

	// Read current static IP allocation configured from dnsmasq host config (if exists).
	deviceStaticFileName := dnsmasq.StaticAllocationFileName(opts.ProjectName, opts.HostName, opts.DeviceName)
	t.currentDHCPMAC, t.currentDHCPv4, t.currentDHCPv6, err = dnsmasq.DHCPStaticAllocation(opts.Network.Name(), deviceStaticFileName)
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	// Set the current allocated IPs internally (these may be changed later), and if they are it will trigger
	// a dnsmasq static host config file rebuild.
	t.allocatedIPv4 = t.currentDHCPv4.IP
	t.allocatedIPv6 = t.currentDHCPv6.IP

	// Get all existing allocations in network if leases file exists. If not then we will detect this later
	// due to the existing allocations maps being nil.
	if shared.PathExists(shared.VarPath("networks", opts.Network.Name(), "dnsmasq.leases")) {
		t.allocationsDHCPv4, t.allocationsDHCPv6, err = dnsmasq.DHCPAllAllocations(opts.Network.Name())
		if err != nil {
			return err
		}
	}

	// Run the supplied allocation function.
	err = f(t)
	if err != nil {
		return err
	}

	// If the user's function didn't fail despite DHCP allocation not being available for either protocol,
	// then presumably they ignored allocation errors. So fail here, rather than try to write the new config
	// out, which will fail with a less helpful error about missing paths.
	if t.allocationsDHCPv4 == nil && t.allocationsDHCPv6 == nil {
		return ErrDHCPNotSupported
	}

	// If MAC or either IPv4 or IPv6 assigned is different than what is in dnsmasq config, rebuild config.
	macChanged := !bytes.Equal(opts.HostMAC, t.currentDHCPMAC)
	ipv4Changed := t.allocatedIPv4 != nil && !t.currentDHCPv4.IP.Equal(t.allocatedIPv4.To4())
	ipv6Changed := t.allocatedIPv6 != nil && !t.currentDHCPv6.IP.Equal(t.allocatedIPv6.To16())

	if macChanged || ipv4Changed || ipv6Changed {
		var IPv4Str, IPv6Str string

		if t.allocatedIPv4 != nil {
			IPv4Str = t.allocatedIPv4.String()
		}

		if t.allocatedIPv6 != nil {
			IPv6Str = t.allocatedIPv6.String()
		}

		// Write out new dnsmasq static host allocation config file.
		err = dnsmasq.UpdateStaticEntry(opts.Network.Name(), opts.ProjectName, opts.HostName, opts.DeviceName, opts.Network.Config(), opts.HostMAC.String(), IPv4Str, IPv6Str)
		if err != nil {
			return err
		}
		l.Debug("Updated static DHCP entry", logger.Ctx{"mac": opts.HostMAC.String(), "IPv4": IPv4Str, "IPv6": IPv6Str})

		// Reload dnsmasq.
		err = dnsmasq.Kill(opts.Network.Name(), true)
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

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

type testcase struct {
	dirs []string
}

func testMkdir(t *testing.T, tc testcase) {
	tmpDir, err := ioutil.TempDir("", "nash-mkdir")
	if err != nil {
		t.Fatal(err)
	}

	defer os.RemoveAll(tmpDir)
	var dirs []string
	for _, p := range tc.dirs {
		dirs = append(dirs, filepath.FromSlash(path.Join(tmpDir, p)))
	}

	err = mkdirs(dirs)
	if err != nil {
		t.Fatal(err)
	}

	for _, d := range dirs {
		if s, err := os.Stat(d); err != nil {
			t.Fatal(err)
		} else if s.Mode()&os.ModeDir != os.ModeDir {
			t.Fatalf("Invalid directory mode: %v", s.Mode())
		}
	}
}

func TestMkdir(t *testing.T) {
	for _, tc := range []testcase{
		{
			dirs: []string{},
		},
		{
			dirs: []string{
				"1", "2", "3", "4", "5",
				"some", "thing", "_random_",
				"_",
			},
		},
		{
			dirs: []string{},
		},
	} {
		tc := tc
		testMkdir(t, tc)
	}
}<commit_msg>test dir already exists<commit_after>package main

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

type testcase struct {
	dirs []string
}

func testMkdir(t *testing.T, tc testcase) {
	tmpDir, err := ioutil.TempDir("", "nash-mkdir")
	if err != nil {
		t.Fatal(err)
	}

	defer os.RemoveAll(tmpDir)
	var dirs []string
	for _, p := range tc.dirs {
		dirs = append(dirs, filepath.FromSlash(path.Join(tmpDir, p)))
	}

	err = mkdirs(dirs)
	if err != nil {
		t.Fatal(err)
	}

	for _, d := range dirs {
		if s, err := os.Stat(d); err != nil {
			t.Fatal(err)
		} else if s.Mode()&os.ModeDir != os.ModeDir {
			t.Fatalf("Invalid directory mode: %v", s.Mode())
		}
	}
}

func TestMkdir(t *testing.T) {
	for _, tc := range []testcase{
		{
			dirs: []string{},
		},
		{
			dirs: []string{
				"1", "2", "3", "4", "5",
				"some", "thing", "_random_",
				"_",
			},
		},
		{
			dirs: []string{"a", "a"}, // directory already exists, silently works
		},
	} {
		tc := tc
		testMkdir(t, tc)
	}
}<|endoftext|>


<commit_before>package dns

import (
	"fmt"
	"strconv"

	r "github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func testCheckAttrStringArray(name, key string, value []string) r.TestCheckFunc {
	return func(s *terraform.State) error {
		ms := s.RootModule()
		rs, ok := ms.Resources[name]
		if !ok {
			return fmt.Errorf("Not found: %s", name)
		}

		is := rs.Primary
		if is == nil {
			return fmt.Errorf("No primary instance: %s", name)
		}

		attrKey := fmt.Sprintf("%s.#", key)
		count, ok := is.Attributes[attrKey]
		if !ok {
			return fmt.Errorf("Attributes not found for %s", attrKey)
		}

		got, _ := strconv.Atoi(count)
		if got != len(value) {
			return fmt.Errorf("Mismatch array count for %s: got %s, wanted %d", key, count, len(value))
		}

		for i, want := range value {
			attrKey = fmt.Sprintf("%s.%d", key, i)
			got, ok := is.Attributes[attrKey]
			if !ok {
				return fmt.Errorf("Missing array item for %s", attrKey)
			}
			if got != want {
				return fmt.Errorf(
					"Mismatched array item for %s: got %s, want %s",
					attrKey,
					got,
					want)
			}
		}

		return nil
	}
}
<commit_msg>update string array test helper to not care about order<commit_after>package dns

import (
	"fmt"
	"strconv"

	r "github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func testCheckAttrStringArray(name, key string, value []string) r.TestCheckFunc {
	return func(s *terraform.State) error {
		ms := s.RootModule()
		rs, ok := ms.Resources[name]
		if !ok {
			return fmt.Errorf("Not found: %s", name)
		}

		is := rs.Primary
		if is == nil {
			return fmt.Errorf("No primary instance: %s", name)
		}

		attrKey := fmt.Sprintf("%s.#", key)
		count, ok := is.Attributes[attrKey]
		if !ok {
			return fmt.Errorf("Attributes not found for %s", attrKey)
		}

		gotCount, _ := strconv.Atoi(count)
		if gotCount != len(value) {
			return fmt.Errorf("Mismatch array count for %s: got %s, wanted %d", key, count, len(value))
		}

	Next:
		for i := 0; i < gotCount; i++ {
			attrKey = fmt.Sprintf("%s.%d", key, i)
			got, ok := is.Attributes[attrKey]
			if !ok {
				return fmt.Errorf("Missing array item for %s", attrKey)
			}
			for _, want := range value {
				if got == want {
					continue Next
				}
			}
			return fmt.Errorf(
				"Unexpected array item for %s: got %s",
				attrKey,
				got)
		}

		return nil
	}
}
<|endoftext|>


<commit_before>// This program simulates actions of a toy robot on to 5x5 tabletop, operating according to a set of given commands.

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	// ensure input commands filename entered on command line
	if len(os.Args) <= 1 {
		fmt.Println("Error: please enter the robot operations command file name on command-line (e.g. > go run RobotMovement.go commands.txt)")
		return
	}

	// capture input file name from command line
	inFileName := os.Args[1]
	file, err := os.Open(inFileName)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Error reading input file: %v\n", err)
		return
	}

	defer file.Close()

	// setup scanner to read commands input file
	scanner := bufio.NewScanner(file)

	// create new robot 🤖
	c_3PO := createRobot()

	// iterate through the commands
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())

		if line != "" {
			// split the command string by whitespace delimiter to obtain command name (and if applicable any arguments)
			line_split := strings.Split(line, " ")
			command := strings.TrimSpace(line_split[0])

			if command == "PLACE" {
				if len(line_split) == 2 {
					// if command is PLACE and there are arguments provided, split arguments substring by comma delimiter to obtain placement coordinates and initial facing
					place_coords := strings.Split(line_split[1], ",")

					if len(place_coords) == 3 {
						// consider PLACE arguments if three are provided (x, y, facing direction)

						// parse x-coordinate of placement command - must be a valid number
						place_x, err := strconv.Atoi(strings.TrimSpace(place_coords[0]))
						if err != nil {
							fmt.Fprintf(os.Stderr, "Placement X-coord argument error: %v\n", err)
							continue
						}

						// parse x-coordinate of placement command - must be a valid number
						place_y, err := strconv.Atoi(strings.TrimSpace(place_coords[1]))
						if err != nil {
							fmt.Fprintf(os.Stderr, "Placement Y-coord argument error: %v\n", err)
							continue
						}

						// parse facing alignment of placement command
						place_facing := strings.TrimSpace(place_coords[2])

						// place robot and capture any placement error
						err = c_3PO.place(place_x, place_y, place_facing)
						if err != nil {
							fmt.Fprintf(os.Stderr, "Placement error: %v\n", err)
						}
					}
				}
			}

			if command == "MOVE" {
				err := c_3PO.move()

				if err != nil {
					fmt.Fprintf(os.Stderr, "Movement error: %v\n", err)
				}
			}

			if command == "LEFT" {
				err := c_3PO.left()

				if err != nil {
					fmt.Fprintf(os.Stderr, "Rotation error: %v\n", err)
				}
			}

			if command == "RIGHT" {
				err := c_3PO.right()

				if err != nil {
					fmt.Fprintf(os.Stderr, "Rotation error: %v\n", err)
				}
			}

			if command == "REPORT" {
				current_x, current_y, current_facing, err := c_3PO.report()

				if err != nil {
					fmt.Fprintf(os.Stderr, "Report error: %v\n", err)
					continue
				}

				fmt.Printf("%d,%d,%s\n", current_x, current_y, current_facing)
			}
		}
	}
}

// ----------------- end main() --------------------

// type struct to represent a robot
type robot struct {
	X      int    // x-coordinate of current position
	Y      int    // y-coordinate of current position
	F      string // current facing direction (NORTH, SOUTH, EAST or WEST)
	Placed bool   // indicator whether the robot is placed on the table or not

	// maximum values of X- and Y-coordinatess the robot can travel - minimum assumed to be 0
	XLIMIT int
	YLIMIT int
}

// maker function to create and return a reference to a robot instance
func createRobot() *robot {
	var r robot
	r.X = 0
	r.Y = 0
	r.F = "NONE"
	r.Placed = false

	// table is 5x5
	r.XLIMIT = 5
	r.YLIMIT = 5

	return &r
}

// ----------- Methods of robot type (place, move, left, right, report) -----------
func (r *robot) place(x int, y int, facing string) error {
	if x >= 0 && x <= r.XLIMIT && y >= 0 && y <= r.YLIMIT && (facing == "NORTH" || facing == "EAST" || facing == "SOUTH" || facing == "WEST") {
		r.X = x
		r.Y = y
		r.F = facing
		r.Placed = true
		return nil
	}

	return fmt.Errorf("Robot placement attempted with invalid initial coordinates (X: %d, Y: %d, FACING: %s)", r.X, r.Y, r.F)
}

func (r *robot) move() error {
	// move robot one unit in the facing direction, with regards to table limitations and returns an error if fails to do so
	if !r.Placed {
		// if robot not placed on table, ignore move command
		return fmt.Errorf("move() called on unplaced robot.")
	}

	if r.F == "NORTH" && r.Y < r.YLIMIT {
		// move 1 unit NORTH
		r.Y++
		return nil
	}

	if r.F == "EAST" && r.X < r.XLIMIT {
		// move 1 unit EAST
		r.X++
		return nil
	}

	if r.F == "SOUTH" && r.Y > 0 {
		// move 1 unit SOUTH
		r.Y--
		return nil
	}

	if r.F == "WEST" && r.X > 0 {
		// move 1 unit WEST
		r.X--
		return nil
	}

	// if program execution reaches this point it would mean that the robot was placed on table but has a facing of neither defined value (NORTH, EAST, SOUTH or WEST) - return error
	return fmt.Errorf("move() called on robot with undefined initial facing or facing towards edge of table (X: %d, Y: %d, FACING: %s)", r.X, r.Y, r.F)
}

func (r *robot) left() error {
	// rotate robot 90degrees counter-clockwise and returns an error if failed to do so

	if !r.Placed {
		// if robot not placed on table, ignore left command
		return fmt.Errorf("left() called on unplaced robot.")
	}

	if r.F == "NORTH" {
		r.F = "WEST"
		return nil
	}

	if r.F == "EAST" {
		r.F = "NORTH"
		return nil
	}

	if r.F == "SOUTH" {
		r.F = "EAST"
		return nil
	}

	if r.F == "WEST" {
		r.F = "SOUTH"
		return nil
	}

	// if program execution reaches this point it would mean that the robot was placed on table but has a facing of neither defined value (NORTH, EAST, SOUTH or WEST) - return error
	return fmt.Errorf("left() called on robot with possibly undefined initial facing (X: %d, Y: %d, FACING: %s)", r.X, r.Y, r.F)
}

func (r *robot) right() error {
	// rotate robot 90degrees clockwise and returns an error if filed to do so

	if !r.Placed {
		// if robot not placed on table, ignore right command
		return fmt.Errorf("right() called on unplaced robot.")
	}

	if r.F == "NORTH" {
		r.F = "EAST"
		return nil
	}

	if r.F == "EAST" {
		r.F = "SOUTH"
		return nil
	}

	if r.F == "SOUTH" {
		r.F = "WEST"
		return nil
	}

	if r.F == "WEST" {
		r.F = "NORTH"
		return nil
	}

	// if program execution reaches this point it would mean that the robot was placed on table but has a facing of neither defined value (NORTH, EAST, SOUTH or WEST) - return error
	return fmt.Errorf("right() called on robot with possibly undefined initial facing (X: %d, Y: %d, FACING: %s)", r.X, r.Y, r.F)
}

func (r *robot) report() (int, int, string, error) {
	// reports the robot's location along with an error if unplaced
	var err error
	err = nil
	if !r.Placed {
		// return error if robot is not placed
		err = fmt.Errorf("report() called on unplaced robot.")
	}

	return r.X, r.Y, r.F, err
}

// ----------------- end methods of robot type ----------------------
<commit_msg>Final changes<commit_after>// This program simulates actions of a toy robot on to 5x5 tabletop, operating according to a set of given commands.

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	// ensure input commands filename entered on command line
	if len(os.Args) <= 1 {
		fmt.Println("Error: please enter the robot operations command file name on command-line (e.g. > go run RobotMovement.go commands.txt)")
		return
	}

	// capture input file name from command line
	inFileName := os.Args[1]
	file, err := os.Open(inFileName)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Error reading input file: %v\n", err)
		return
	}

	defer file.Close()

	// setup scanner to read commands input file
	scanner := bufio.NewScanner(file)

	// create new robot 🤖
	c_3PO := createRobot()

	// iterate through the commands
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())

		if line != "" {
			// split the command string by whitespace delimiter to obtain command name (and if applicable any arguments)
			line_split := strings.Split(line, " ")
			command := strings.TrimSpace(line_split[0])

			if command == "PLACE" {
				if len(line_split) == 2 {
					// if command is PLACE and there are arguments provided, split arguments substring by comma delimiter to obtain placement coordinates and initial facing
					place_coords := strings.Split(line_split[1], ",")

					if len(place_coords) == 3 {
						// consider PLACE arguments if three are provided (x, y, facing direction)

						// parse x-coordinate of placement command - must be a valid number
						place_x, err := strconv.Atoi(strings.TrimSpace(place_coords[0]))
						if err != nil {
							fmt.Fprintf(os.Stderr, "Placement X-coord argument error: %v\n", err)
							continue
						}

						// parse x-coordinate of placement command - must be a valid number
						place_y, err := strconv.Atoi(strings.TrimSpace(place_coords[1]))
						if err != nil {
							fmt.Fprintf(os.Stderr, "Placement Y-coord argument error: %v\n", err)
							continue
						}

						// parse facing alignment of placement command
						place_facing := strings.TrimSpace(place_coords[2])

						// place robot and capture any placement error
						err = c_3PO.place(place_x, place_y, place_facing)
						if err != nil {
							fmt.Fprintf(os.Stderr, "Placement error: %v\n", err)
						}
					}
				}
			}

			if command == "MOVE" {
				err := c_3PO.move()

				if err != nil {
					fmt.Fprintf(os.Stderr, "Movement error: %v\n", err)
				}
			}

			if command == "LEFT" {
				err := c_3PO.left()

				if err != nil {
					fmt.Fprintf(os.Stderr, "Rotation error: %v\n", err)
				}
			}

			if command == "RIGHT" {
				err := c_3PO.right()

				if err != nil {
					fmt.Fprintf(os.Stderr, "Rotation error: %v\n", err)
				}
			}

			if command == "REPORT" {
				current_x, current_y, current_facing, err := c_3PO.report()

				if err != nil {
					fmt.Fprintf(os.Stderr, "Report error: %v\n", err)
					continue
				}

				fmt.Printf("%d,%d,%s\n", current_x, current_y, current_facing)
			}
		}
	}
}

// ----------------- end main() --------------------

// type struct to represent a robot
type robot struct {
	X      int    // x-coordinate of current position
	Y      int    // y-coordinate of current position
	F      string // current facing direction (NORTH, SOUTH, EAST or WEST)
	Placed bool   // indicator whether the robot is placed on the table or not

	// maximum values of X- and Y-coordinatess the robot can travel - minimum assumed to be 0
	XLIMIT int
	YLIMIT int
}

// maker function to create and return a reference to a robot instance
func createRobot() *robot {
	var r robot
	r.X = 0
	r.Y = 0
	r.F = "NONE"
	r.Placed = false

	// table is 5x5
	r.XLIMIT = 5
	r.YLIMIT = 5

	return &r
}

// ----------- Methods of robot type (place, move, left, right, report) -----------
func (r *robot) place(x int, y int, facing string) error {
	if x >= 0 && x <= r.XLIMIT && y >= 0 && y <= r.YLIMIT && (facing == "NORTH" || facing == "EAST" || facing == "SOUTH" || facing == "WEST") {
		r.X = x
		r.Y = y
		r.F = facing
		r.Placed = true
		return nil
	}

	return fmt.Errorf("Robot placement attempted with invalid coordinates (X: %d, Y: %d, FACING: %s)", x, y, facing)
}

func (r *robot) move() error {
	// move robot one unit in the facing direction, with regards to table limitations and returns an error if fails to do so
	if !r.Placed {
		// if robot not placed on table, ignore move command
		return fmt.Errorf("move() called on unplaced robot.")
	}

	if r.F == "NORTH" && r.Y < r.YLIMIT {
		// move 1 unit NORTH
		r.Y++
		return nil
	}

	if r.F == "EAST" && r.X < r.XLIMIT {
		// move 1 unit EAST
		r.X++
		return nil
	}

	if r.F == "SOUTH" && r.Y > 0 {
		// move 1 unit SOUTH
		r.Y--
		return nil
	}

	if r.F == "WEST" && r.X > 0 {
		// move 1 unit WEST
		r.X--
		return nil
	}

	// if program execution reaches this point it would mean that the robot was placed on table but has a facing of neither defined value (NORTH, EAST, SOUTH or WEST) - return error
	return fmt.Errorf("move() called on robot with undefined initial facing or facing towards edge of table (X: %d, Y: %d, FACING: %s)", r.X, r.Y, r.F)
}

func (r *robot) left() error {
	// rotate robot 90degrees counter-clockwise and returns an error if failed to do so

	if !r.Placed {
		// if robot not placed on table, ignore left command
		return fmt.Errorf("left() called on unplaced robot.")
	}

	if r.F == "NORTH" {
		r.F = "WEST"
		return nil
	}

	if r.F == "EAST" {
		r.F = "NORTH"
		return nil
	}

	if r.F == "SOUTH" {
		r.F = "EAST"
		return nil
	}

	if r.F == "WEST" {
		r.F = "SOUTH"
		return nil
	}

	// if program execution reaches this point it would mean that the robot was placed on table but has a facing of neither defined value (NORTH, EAST, SOUTH or WEST) - return error
	return fmt.Errorf("left() called on robot with possibly undefined initial facing (X: %d, Y: %d, FACING: %s)", r.X, r.Y, r.F)
}

func (r *robot) right() error {
	// rotate robot 90degrees clockwise and returns an error if filed to do so

	if !r.Placed {
		// if robot not placed on table, ignore right command
		return fmt.Errorf("right() called on unplaced robot.")
	}

	if r.F == "NORTH" {
		r.F = "EAST"
		return nil
	}

	if r.F == "EAST" {
		r.F = "SOUTH"
		return nil
	}

	if r.F == "SOUTH" {
		r.F = "WEST"
		return nil
	}

	if r.F == "WEST" {
		r.F = "NORTH"
		return nil
	}

	// if program execution reaches this point it would mean that the robot was placed on table but has a facing of neither defined value (NORTH, EAST, SOUTH or WEST) - return error
	return fmt.Errorf("right() called on robot with possibly undefined initial facing (X: %d, Y: %d, FACING: %s)", r.X, r.Y, r.F)
}

func (r *robot) report() (int, int, string, error) {
	// reports the robot's location along with an error if unplaced
	var err error
	err = nil
	if !r.Placed {
		// return error if robot is not placed
		err = fmt.Errorf("report() called on unplaced robot.")
	}

	return r.X, r.Y, r.F, err
}

// ----------------- end methods of robot type ----------------------
<|endoftext|>


<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// This package provides helpers for testing with resources.
package resourcetesting

import (
	"io"
	"strings"
	"time"

	"github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	"github.com/juju/testing/filetesting"
	gc "gopkg.in/check.v1"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"

	"github.com/juju/juju/resource"
)

// NewResource produces full resource info for the given name and
// content. The origin is set set to "upload". A reader is also returned
// which contains the content.
func NewResource(c *gc.C, stub *testing.Stub, name, applicationID, content string) resource.Opened {
	username := "a-user"
	return resource.Opened{
		Resource:   newResource(c, name, applicationID, username, content),
		ReadCloser: newStubReadCloser(stub, content),
	}
}

// NewCharmResource produces basic resource info for the given name
// and content. The origin is set set to "upload".
func NewCharmResource(c *gc.C, name, content string) charmresource.Resource {
	fp, err := charmresource.GenerateFingerprint(strings.NewReader(content))
	c.Assert(err, jc.ErrorIsNil)
	res := charmresource.Resource{
		Meta: charmresource.Meta{
			Name: name,
			Type: charmresource.TypeFile,
			Path: name + ".tgz",
		},
		Origin:      charmresource.OriginUpload,
		Revision:    0,
		Fingerprint: fp,
		Size:        int64(len(content)),
	}
	err = res.Validate()
	c.Assert(err, jc.ErrorIsNil)

	return res
}

// NewPlaceholderResource returns resource info for a resource that
// has not been uploaded or pulled from the charm store yet. The origin
// is set to "upload".
func NewPlaceholderResource(c *gc.C, name, applicationID string) resource.Resource {
	res := newResource(c, name, applicationID, "", "")
	res.Fingerprint = charmresource.Fingerprint{}
	return res
}

func newResource(c *gc.C, name, applicationID, username, content string) resource.Resource {
	var timestamp time.Time
	if username != "" {
		// TODO(perrito666) 2016-05-02 lp:1558657
		timestamp = time.Now().UTC()
	}
	res := resource.Resource{
		Resource:      NewCharmResource(c, name, content),
		ID:            applicationID + "/" + name,
		PendingID:     "",
		ApplicationID: applicationID,
		Username:      username,
		Timestamp:     timestamp,
	}
	err := res.Validate()
	c.Assert(err, jc.ErrorIsNil)
	return res
}

type stubReadCloser struct {
	io.Reader
	io.Closer
}

func newStubReadCloser(stub *testing.Stub, content string) io.ReadCloser {
	return &stubReadCloser{
		Reader: filetesting.NewStubReader(stub, content),
		Closer: &filetesting.StubCloser{
			Stub: stub,
		},
	}
}
<commit_msg>resource/resourcetesting: Desc for test resources<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// This package provides helpers for testing with resources.
package resourcetesting

import (
	"io"
	"strings"
	"time"

	"github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	"github.com/juju/testing/filetesting"
	gc "gopkg.in/check.v1"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"

	"github.com/juju/juju/resource"
)

// NewResource produces full resource info for the given name and
// content. The origin is set set to "upload". A reader is also returned
// which contains the content.
func NewResource(c *gc.C, stub *testing.Stub, name, applicationID, content string) resource.Opened {
	username := "a-user"
	return resource.Opened{
		Resource:   newResource(c, name, applicationID, username, content),
		ReadCloser: newStubReadCloser(stub, content),
	}
}

// NewCharmResource produces basic resource info for the given name
// and content. The origin is set set to "upload".
func NewCharmResource(c *gc.C, name, content string) charmresource.Resource {
	fp, err := charmresource.GenerateFingerprint(strings.NewReader(content))
	c.Assert(err, jc.ErrorIsNil)
	res := charmresource.Resource{
		Meta: charmresource.Meta{
			Name:        name,
			Type:        charmresource.TypeFile,
			Path:        name + ".tgz",
			Description: name + " description",
		},
		Origin:      charmresource.OriginUpload,
		Revision:    0,
		Fingerprint: fp,
		Size:        int64(len(content)),
	}
	err = res.Validate()
	c.Assert(err, jc.ErrorIsNil)

	return res
}

// NewPlaceholderResource returns resource info for a resource that
// has not been uploaded or pulled from the charm store yet. The origin
// is set to "upload".
func NewPlaceholderResource(c *gc.C, name, applicationID string) resource.Resource {
	res := newResource(c, name, applicationID, "", "")
	res.Fingerprint = charmresource.Fingerprint{}
	return res
}

func newResource(c *gc.C, name, applicationID, username, content string) resource.Resource {
	var timestamp time.Time
	if username != "" {
		// TODO(perrito666) 2016-05-02 lp:1558657
		timestamp = time.Now().UTC()
	}
	res := resource.Resource{
		Resource:      NewCharmResource(c, name, content),
		ID:            applicationID + "/" + name,
		PendingID:     "",
		ApplicationID: applicationID,
		Username:      username,
		Timestamp:     timestamp,
	}
	err := res.Validate()
	c.Assert(err, jc.ErrorIsNil)
	return res
}

type stubReadCloser struct {
	io.Reader
	io.Closer
}

func newStubReadCloser(stub *testing.Stub, content string) io.ReadCloser {
	return &stubReadCloser{
		Reader: filetesting.NewStubReader(stub, content),
		Closer: &filetesting.StubCloser{
			Stub: stub,
		},
	}
}
<|endoftext|>


<commit_before>// rule applies rules to wordlists
// it handles UTF-8 rules such as appending $世
package main

import (
	"bufio"
	"fmt"
	"os"
	"runtime"
	"sync"

	"github.com/coolbry95/passutils/ruleprocessor/rules"
)

func main() {

	if len(os.Args) == 1 {
		fmt.Printf("%s rules list", os.Args[0])
		os.Exit(-1)
	}

	rulefile, err := os.Open(os.Args[1])
	if err != nil {
		fmt.Println(err.Error())
	}
	wordlist, err := os.Open(os.Args[2])
	if err != nil {
		fmt.Println(err.Error())
	}

	scanner := bufio.NewScanner(rulefile)
	r := make([][]string, 0, 100)
	for scanner.Scan() {
		temp := rules.ParseLine(scanner.Text())
		if temp != nil {
			r = append(r, temp)
		}
	}

	p := make(chan string, runtime.NumCPU())
	out := make(chan string, runtime.NumCPU())

	var wg sync.WaitGroup

	go func() {
		for print := range out {
			if print == "" {
				continue
			}
			fmt.Println(print)
		}
	}()

	for i := 0; i < runtime.NumCPU(); i++ {
		go func() {
			for pass := range p {
				for _, ru := range r {
					out <- rules.ApplyRules(ru, pass)
				}
			}
			wg.Done()
		}()
		wg.Add(1)
	}

	scanning := bufio.NewScanner(wordlist)
	for scanning.Scan() {
		p <- scanning.Text()

	}

	close(p)
	wg.Wait()
	close(out)
}
<commit_msg>removed the sync dependency as it was not needed.<commit_after>// rule applies rules to wordlists
// it handles UTF-8 rules such as appending $世
package main

import (
	"bufio"
	"fmt"
	"os"
	"runtime"

	"github.com/coolbry95/passutils/ruleprocessor/rules"
)

func main() {

	if len(os.Args) == 1 {
		fmt.Printf("%s rules list", os.Args[0])
		os.Exit(-1)
	}

	rulefile, err := os.Open(os.Args[1])
	if err != nil {
		fmt.Println(err.Error())
	}
	wordlist, err := os.Open(os.Args[2])
	if err != nil {
		fmt.Println(err.Error())
	}

	scanner := bufio.NewScanner(rulefile)
	r := make([][]string, 0, 100)
	for scanner.Scan() {
		temp := rules.ParseLine(scanner.Text())
		if temp != nil {
			r = append(r, temp)
		}
	}

	p := make(chan string, runtime.NumCPU())
	out := make(chan string, runtime.NumCPU())

	go func() {
		for print := range out {
			if print == "" {
				continue
			}
			fmt.Println(print)
		}
	}()

	for i := 0; i < runtime.NumCPU(); i++ {
		go func() {
			for pass := range p {
				for _, ru := range r {
					out <- rules.ApplyRules(ru, pass)
				}
			}
		}()
	}

	scanning := bufio.NewScanner(wordlist)
	for scanning.Scan() {
		p <- scanning.Text()

	}

	close(p)
	close(out)
}
<|endoftext|>


<commit_before>package solr_test

import (
	"strings"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/sendgrid/go-solr"
)

var _ = Describe("Solr Client", func() {
	var solrClient solr.SolrZK
	var solrHttp solr.SolrHTTP
	var solrHttpRetrier solr.SolrHTTP
	var locator solr.SolrLocator
	solrClient = solr.NewSolrZK("zk:2181", "solr", "solrtest")
	locator = solrClient.GetSolrLocator()

	var err error
	err = solrClient.Listen()
	BeforeEach(func() {
		Expect(err).To(BeNil())
		https, _ := solrClient.UseHTTPS()
		solrHttp, err = solr.NewSolrHTTP(https, "solrtest", solr.User("solr"), solr.Password("admin"), solr.MinRF(2))
		Expect(err).To(BeNil())
		solrHttpRetrier = solr.NewSolrHttpRetrier(solrHttp, 5, 100*time.Millisecond)
	})
	It("construct", func() {
		solrClient := solr.NewSolrZK("test", "solr", "solrtest")
		Expect(solrClient).To(Not(BeNil()))
		err := solrClient.Listen()
		Expect(err).To(Not(BeNil()))

	})

	Describe("Test Connection", func() {

		It("can get clusterstate", func() {
			state, err := solrClient.GetClusterState()
			Expect(err).To(BeNil())
			Expect(state).To(Not(BeNil()))
			Expect(state.Version > 0).To(BeTrue())
			Expect(len(state.Collections)).To(Equal(1))
		})

		It("can find a leader", func() {
			state, err := solrClient.GetClusterState()
			Expect(err).To(BeNil())
			Expect(state).To(Not(BeNil()))
			Expect(len(state.Collections)).To(Equal(1))
			leaders, err := locator.GetLeaders("mycrazyshardkey1!test.1@test.com")
			Expect(err).To(BeNil())
			leader := leaders[0]
			Expect(leader).To(Not(BeNil()))
			Expect(leader).To(ContainSubstring(":8983/solr"))
			Expect(leader).To(ContainSubstring("http://"))
		})

		It("can find a replica", func() {
			state, err := solrClient.GetClusterState()
			Expect(err).To(BeNil())
			Expect(state).To(Not(BeNil()))
			Expect(len(state.Collections)).To(Equal(1))
			replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey1!")
			Expect(err).To(BeNil())
			Expect(len(replicas)).To(Not(BeZero()))

			Expect(replicas[0]).To(ContainSubstring(":8983/solr"))
			Expect(replicas[0]).To(ContainSubstring("http://"))
		})

		Describe("Test Requests", func() {
			It("can get requests", func() {
				replicas, err := locator.GetReplicaUris()
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.FilterQuery("*:*"), solr.Rows(10))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
			})
			It("can update requests", func() {
				uuid, _ := newUUID()

				doc := map[string]interface{}{
					"id":         "mycrazyshardkey1!" + uuid,
					"email":      uuid + "feldman1@sendgrid.com",
					"first_name": "shawn1" + uuid,
					"last_name":  uuid + "feldman1",
				}
				leader, err := locator.GetLeaders("mycrazyshardkey1!" + uuid)
				Expect(err).To(BeNil())
				err = solrHttp.Update(leader, true, doc, solr.Commit(true))
				Expect(err).To(BeNil())
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey1!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Query("*:*"), solr.FilterQuery("first_name:shawn1"+uuid), solr.Rows(10))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(1))
			})

			It("can update requests with no doc id", func() {
				uuid, _ := newUUID()

				doc := map[string]interface{}{
					"id":         "mycrazyshardkey1!" + uuid,
					"email":      uuid + "feldman1@sendgrid.com",
					"first_name": "shawn1" + uuid,
					"last_name":  uuid + "feldman1",
				}
				leader, err := locator.GetLeaders("mycrazyshardkey1!" + uuid)
				Expect(err).To(BeNil())
				err = solrHttp.Update(leader, true, doc, solr.Commit(true))
				Expect(err).To(BeNil())
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey1!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Query("*:*"), solr.FilterQuery("first_name:shawn1"+uuid), solr.Rows(10))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(1))
			})

			It("can update requests with route", func() {
				uuid, _ := newUUID()

				doc := map[string]interface{}{
					"id":         "mycrazyshardkey1!" + uuid,
					"email":      uuid + "feldman1@sendgrid.com",
					"first_name": "shawn1" + uuid,
					"last_name":  uuid + "feldman1",
				}
				leader, err := locator.GetLeaders("mycrazyshardkey1!" + uuid)
				Expect(err).To(BeNil())
				err = solrHttp.Update(leader, true, doc, solr.Commit(true), solr.Route("mycrazyshardkey1!"))
				Expect(err).To(BeNil())
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey1!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Query("*:*"), solr.FilterQuery("first_name:shawn1"+uuid), solr.Rows(10))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(1))
			})

			It("can update requests with route with version", func() {
				uuid, _ := newUUID()

				doc := map[string]interface{}{
					"id":         "mycrazyshardkey1!" + uuid,
					"email":      uuid + "feldman1@sendgrid.com",
					"first_name": "shawn1" + uuid,
					"last_name":  uuid + "feldman1",
				}
				state, err := solrClient.GetClusterState()
				Expect(err).To(BeNil())
				leader, err := locator.GetLeaders("mycrazyshardkey1!" + uuid)
				Expect(err).To(BeNil())
				err = solrHttp.Update(leader, true, doc, solr.Commit(true), solr.Route("mycrazyshardkey1!"), solr.ClusterStateVersion(state.Version, "goseg"))
				Expect(err).To(BeNil())
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey1!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Query("*:*"), solr.FilterQuery("first_name:shawn1"+uuid), solr.Rows(10), solr.ClusterStateVersion(state.Version, "goseg"))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(1))
			})

			It("can update requests and read with route", func() {
				uuid, _ := newUUID()

				doc := map[string]interface{}{
					"id":         "mycrazyshardkey3!" + uuid,
					"email":      uuid + "feldman@sendgrid.com",
					"first_name": "shawn3" + uuid,
					"last_name":  uuid,
				}
				leader, err := locator.GetLeaders("mycrazyshardkey3!" + uuid)
				Expect(err).To(BeNil())
				err = solrHttp.Update(leader, true, doc, solr.Commit(true))
				Expect(err).To(BeNil())
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey3!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Query("*:*"), solr.FilterQuery("last_name:"+uuid), solr.Rows(10))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(1))
			})

			It("can update requests and read with route many times", func() {
				const limit int = 10
				uuid, _ := newUUID()
				for i := 0; i < limit; i++ {
					iterationId, _ := newUUID()
					doc := map[string]interface{}{
						"id":         "mycrazyshardkey4!rando" + iterationId,
						"email":      "rando" + iterationId + "@sendgrid.com",
						"first_name": "tester" + iterationId,
						"last_name":  uuid,
					}
					if i < limit-1 {
						leader, err := locator.GetLeaders(doc["id"].(string))
						Expect(err).To(BeNil())
						err = solrHttp.Update(leader, true, doc, solr.Commit(false))
						Expect(err).To(BeNil())
					} else {
						leader, err := locator.GetLeaders(doc["id"].(string))
						Expect(err).To(BeNil())
						err = solrHttp.Update(leader, true, doc, solr.Commit(true))
						Expect(err).To(BeNil())
					}

				}
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey4!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Query("*:*"), solr.FilterQuery("last_name:"+uuid), solr.Rows(1000))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(limit))
			})

			It("can test the retrier requests and read with route many times", func() {
				const limit int = 100
				uuid, _ := newUUID()
				for i := 0; i < limit; i++ {
					iterationId, _ := newUUID()
					doc := map[string]interface{}{
						"id":         "mycrazyshardkey4!rando" + iterationId,
						"email":      "rando" + iterationId + "@sendgrid.com",
						"first_name": "tester" + iterationId,
						"last_name":  uuid,
					}
					if i < limit-1 {
						leader, err := locator.GetLeaders(doc["id"].(string))
						Expect(err).To(BeNil())
						err = solrHttpRetrier.Update(leader, true, doc, solr.Commit(false))
						Expect(err).To(BeNil())
					} else {
						leader, err := locator.GetLeaders(doc["id"].(string))
						Expect(err).To(BeNil())
						err = solrHttpRetrier.Update(leader, true, doc, solr.Commit(true))
						Expect(err).To(BeNil())
					}

				}
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey4!")
				Expect(err).To(BeNil())
				r, err := solrHttpRetrier.Select(replicas, solr.Query("*:*"), solr.FilterQuery("last_name:"+uuid), solr.Rows(1000))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(limit))
			})

			It("can delete all", func() {
				lastId := ""
				const limit int = 10
				uuid, _ := newUUID()
				shardKey := "mycrazysha" + uuid
				for i := 0; i < limit; i++ {
					iterationId, _ := newUUID()
					lastId := shardKey + "!rando" + iterationId
					doc := map[string]interface{}{
						"id":         lastId,
						"email":      "rando" + iterationId + "@sendgrid.com",
						"first_name": "tester" + iterationId,
						"last_name":  uuid,
					}
					leader, err := locator.GetLeaders(doc["id"].(string))
					Expect(err).To(BeNil())

					if i < limit-1 {
						err := solrHttp.Update(leader, true, doc, solr.Commit(false))
						Expect(err).To(BeNil())
					} else {
						err := solrHttp.Update(leader, true, doc, solr.Commit(true))
						Expect(err).To(BeNil())
					}

				}
				leader, err := locator.GetLeaders(lastId)
				Expect(err).To(BeNil())
				err = solrHttp.Update(leader, false, nil, solr.Commit(true), solr.DeleteStreamBody("last_name:*"))
				Expect(err).To(BeNil())
				replicas, err := locator.GetReplicasFromRoute(shardKey + "!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Route(shardKey), solr.Query("*:*"), solr.FilterQuery("last_name:"+uuid), solr.Rows(1000))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(0))
			})

			It("can get the shard for a route", func() {
				shard, err := locator.GetShardFromRoute("mycrazyshardkey3!")
				Expect(err).To(BeNil())
				Expect(shard).To(Not(BeNil()))
			})
		})
	})
	Describe("Basic Auth Fails", func() {
		It("can get requests", func() {
			solrNoAuthClient := solr.NewSolrZK("zk:2181", "solr", "solrtest")
			err := solrNoAuthClient.Listen()
			Expect(err).To(BeNil())
			https, _ := solrClient.UseHTTPS()
			solrNoAuthHttp, err := solr.NewSolrHTTP(https, "solrtest")
			Expect(err).To(BeNil())
			err = solrNoAuthClient.Listen()
			Expect(err).To(BeNil())
			replicas, err := locator.GetReplicaUris()
			r, err := solrNoAuthHttp.Select(replicas, solr.FilterQuery("*:*"), solr.Rows(10))
			Expect(err).To(Not(BeNil()))
			Expect(strings.Contains(err.Error(), "401")).To(BeTrue())
			Expect(r.Status).To(BeEquivalentTo(401))
		})

	})

})
<commit_msg>prefer local shards tests<commit_after>package solr_test

import (
	"strings"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/sendgrid/go-solr"
)

var _ = Describe("Solr Client", func() {
	var solrClient solr.SolrZK
	var solrHttp solr.SolrHTTP
	var solrHttpRetrier solr.SolrHTTP
	var locator solr.SolrLocator
	solrClient = solr.NewSolrZK("zk:2181", "solr", "solrtest")
	locator = solrClient.GetSolrLocator()

	var err error
	err = solrClient.Listen()
	BeforeEach(func() {
		Expect(err).To(BeNil())
		https, _ := solrClient.UseHTTPS()
		solrHttp, err = solr.NewSolrHTTP(https, "solrtest", solr.User("solr"), solr.Password("admin"), solr.MinRF(2))
		Expect(err).To(BeNil())
		solrHttpRetrier = solr.NewSolrHttpRetrier(solrHttp, 5, 100*time.Millisecond)
	})
	It("construct", func() {
		solrClient := solr.NewSolrZK("test", "solr", "solrtest")
		Expect(solrClient).To(Not(BeNil()))
		err := solrClient.Listen()
		Expect(err).To(Not(BeNil()))

	})

	Describe("Test Connection", func() {

		It("can get clusterstate", func() {
			state, err := solrClient.GetClusterState()
			Expect(err).To(BeNil())
			Expect(state).To(Not(BeNil()))
			Expect(state.Version > 0).To(BeTrue())
			Expect(len(state.Collections)).To(Equal(1))
		})

		It("can find a leader", func() {
			state, err := solrClient.GetClusterState()
			Expect(err).To(BeNil())
			Expect(state).To(Not(BeNil()))
			Expect(len(state.Collections)).To(Equal(1))
			leaders, err := locator.GetLeaders("mycrazyshardkey1!test.1@test.com")
			Expect(err).To(BeNil())
			leader := leaders[0]
			Expect(leader).To(Not(BeNil()))
			Expect(leader).To(ContainSubstring(":8983/solr"))
			Expect(leader).To(ContainSubstring("http://"))
		})

		It("can find a replica", func() {
			state, err := solrClient.GetClusterState()
			Expect(err).To(BeNil())
			Expect(state).To(Not(BeNil()))
			Expect(len(state.Collections)).To(Equal(1))
			replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey1!")
			Expect(err).To(BeNil())
			Expect(len(replicas)).To(Not(BeZero()))

			Expect(replicas[0]).To(ContainSubstring(":8983/solr"))
			Expect(replicas[0]).To(ContainSubstring("http://"))
		})

		Describe("Test Requests", func() {
			It("can get requests", func() {
				replicas, err := locator.GetReplicaUris()
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.FilterQuery("*:*"), solr.Rows(10))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
			})
			It("can update requests", func() {
				uuid, _ := newUUID()

				doc := map[string]interface{}{
					"id":         "mycrazyshardkey1!" + uuid,
					"email":      uuid + "feldman1@sendgrid.com",
					"first_name": "shawn1" + uuid,
					"last_name":  uuid + "feldman1",
				}
				leader, err := locator.GetLeaders("mycrazyshardkey1!" + uuid)
				Expect(err).To(BeNil())
				err = solrHttp.Update(leader, true, doc, solr.Commit(true))
				Expect(err).To(BeNil())
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey1!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Query("*:*"), solr.FilterQuery("first_name:shawn1"+uuid), solr.Rows(10))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(1))
			})

			It("can update requests with no doc id", func() {
				uuid, _ := newUUID()

				doc := map[string]interface{}{
					"id":         "mycrazyshardkey1!" + uuid,
					"email":      uuid + "feldman1@sendgrid.com",
					"first_name": "shawn1" + uuid,
					"last_name":  uuid + "feldman1",
				}
				leader, err := locator.GetLeaders("mycrazyshardkey1!" + uuid)
				Expect(err).To(BeNil())
				err = solrHttp.Update(leader, true, doc, solr.Commit(true))
				Expect(err).To(BeNil())
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey1!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Query("*:*"), solr.FilterQuery("first_name:shawn1"+uuid), solr.Rows(10))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(1))
			})

			It("can update requests with route", func() {
				uuid, _ := newUUID()

				doc := map[string]interface{}{
					"id":         "mycrazyshardkey1!" + uuid,
					"email":      uuid + "feldman1@sendgrid.com",
					"first_name": "shawn1" + uuid,
					"last_name":  uuid + "feldman1",
				}
				leader, err := locator.GetLeaders("mycrazyshardkey1!" + uuid)
				Expect(err).To(BeNil())
				err = solrHttp.Update(leader, true, doc, solr.Commit(true), solr.Route("mycrazyshardkey1!"))
				Expect(err).To(BeNil())
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey1!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Query("*:*"), solr.FilterQuery("first_name:shawn1"+uuid), solr.Rows(10))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(1))
			})

			It("test prefer loca", func() {
				uuid, _ := newUUID()

				doc := map[string]interface{}{
					"id":         "mycrazyshardkey1!" + uuid,
					"email":      uuid + "feldman1@sendgrid.com",
					"first_name": "shawn1" + uuid,
					"last_name":  uuid + "feldman1",
				}
				leader, err := locator.GetLeaders("mycrazyshardkey1!" + uuid)
				Expect(err).To(BeNil())
				err = solrHttp.Update(leader, true, doc, solr.Commit(true), solr.Route("mycrazyshardkey1!"))
				Expect(err).To(BeNil())
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey1!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Query("*:*"), solr.FilterQuery("first_name:shawn1"+uuid), solr.Rows(10), solr.PreferLocalShards(true))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(1))
			})

			It("can update requests with route with version", func() {
				uuid, _ := newUUID()

				doc := map[string]interface{}{
					"id":         "mycrazyshardkey1!" + uuid,
					"email":      uuid + "feldman1@sendgrid.com",
					"first_name": "shawn1" + uuid,
					"last_name":  uuid + "feldman1",
				}
				state, err := solrClient.GetClusterState()
				Expect(err).To(BeNil())
				leader, err := locator.GetLeaders("mycrazyshardkey1!" + uuid)
				Expect(err).To(BeNil())
				err = solrHttp.Update(leader, true, doc, solr.Commit(true), solr.Route("mycrazyshardkey1!"), solr.ClusterStateVersion(state.Version, "goseg"))
				Expect(err).To(BeNil())
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey1!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Query("*:*"), solr.FilterQuery("first_name:shawn1"+uuid), solr.Rows(10), solr.ClusterStateVersion(state.Version, "goseg"))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(1))
			})

			It("can update requests and read with route", func() {
				uuid, _ := newUUID()

				doc := map[string]interface{}{
					"id":         "mycrazyshardkey3!" + uuid,
					"email":      uuid + "feldman@sendgrid.com",
					"first_name": "shawn3" + uuid,
					"last_name":  uuid,
				}
				leader, err := locator.GetLeaders("mycrazyshardkey3!" + uuid)
				Expect(err).To(BeNil())
				err = solrHttp.Update(leader, true, doc, solr.Commit(true))
				Expect(err).To(BeNil())
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey3!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Query("*:*"), solr.FilterQuery("last_name:"+uuid), solr.Rows(10))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(1))
			})

			It("can update requests and read with route many times", func() {
				const limit int = 10
				uuid, _ := newUUID()
				for i := 0; i < limit; i++ {
					iterationId, _ := newUUID()
					doc := map[string]interface{}{
						"id":         "mycrazyshardkey4!rando" + iterationId,
						"email":      "rando" + iterationId + "@sendgrid.com",
						"first_name": "tester" + iterationId,
						"last_name":  uuid,
					}
					if i < limit-1 {
						leader, err := locator.GetLeaders(doc["id"].(string))
						Expect(err).To(BeNil())
						err = solrHttp.Update(leader, true, doc, solr.Commit(false))
						Expect(err).To(BeNil())
					} else {
						leader, err := locator.GetLeaders(doc["id"].(string))
						Expect(err).To(BeNil())
						err = solrHttp.Update(leader, true, doc, solr.Commit(true))
						Expect(err).To(BeNil())
					}

				}
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey4!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Query("*:*"), solr.FilterQuery("last_name:"+uuid), solr.Rows(1000))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(limit))
			})

			It("can test the retrier requests and read with route many times", func() {
				const limit int = 100
				uuid, _ := newUUID()
				for i := 0; i < limit; i++ {
					iterationId, _ := newUUID()
					doc := map[string]interface{}{
						"id":         "mycrazyshardkey4!rando" + iterationId,
						"email":      "rando" + iterationId + "@sendgrid.com",
						"first_name": "tester" + iterationId,
						"last_name":  uuid,
					}
					if i < limit-1 {
						leader, err := locator.GetLeaders(doc["id"].(string))
						Expect(err).To(BeNil())
						err = solrHttpRetrier.Update(leader, true, doc, solr.Commit(false))
						Expect(err).To(BeNil())
					} else {
						leader, err := locator.GetLeaders(doc["id"].(string))
						Expect(err).To(BeNil())
						err = solrHttpRetrier.Update(leader, true, doc, solr.Commit(true))
						Expect(err).To(BeNil())
					}

				}
				replicas, err := locator.GetReplicasFromRoute("mycrazyshardkey4!")
				Expect(err).To(BeNil())
				r, err := solrHttpRetrier.Select(replicas, solr.Query("*:*"), solr.FilterQuery("last_name:"+uuid), solr.Rows(1000))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(limit))
			})

			It("can delete all", func() {
				lastId := ""
				const limit int = 10
				uuid, _ := newUUID()
				shardKey := "mycrazysha" + uuid
				for i := 0; i < limit; i++ {
					iterationId, _ := newUUID()
					lastId := shardKey + "!rando" + iterationId
					doc := map[string]interface{}{
						"id":         lastId,
						"email":      "rando" + iterationId + "@sendgrid.com",
						"first_name": "tester" + iterationId,
						"last_name":  uuid,
					}
					leader, err := locator.GetLeaders(doc["id"].(string))
					Expect(err).To(BeNil())

					if i < limit-1 {
						err := solrHttp.Update(leader, true, doc, solr.Commit(false))
						Expect(err).To(BeNil())
					} else {
						err := solrHttp.Update(leader, true, doc, solr.Commit(true))
						Expect(err).To(BeNil())
					}

				}
				leader, err := locator.GetLeaders(lastId)
				Expect(err).To(BeNil())
				err = solrHttp.Update(leader, false, nil, solr.Commit(true), solr.DeleteStreamBody("last_name:*"))
				Expect(err).To(BeNil())
				replicas, err := locator.GetReplicasFromRoute(shardKey + "!")
				Expect(err).To(BeNil())
				r, err := solrHttp.Select(replicas, solr.Route(shardKey), solr.Query("*:*"), solr.FilterQuery("last_name:"+uuid), solr.Rows(1000))
				Expect(err).To(BeNil())
				Expect(r).To(Not(BeNil()))
				Expect(r.Response.NumFound).To(BeEquivalentTo(0))
			})

			It("can get the shard for a route", func() {
				shard, err := locator.GetShardFromRoute("mycrazyshardkey3!")
				Expect(err).To(BeNil())
				Expect(shard).To(Not(BeNil()))
			})
		})
	})
	Describe("Basic Auth Fails", func() {
		It("can get requests", func() {
			solrNoAuthClient := solr.NewSolrZK("zk:2181", "solr", "solrtest")
			err := solrNoAuthClient.Listen()
			Expect(err).To(BeNil())
			https, _ := solrClient.UseHTTPS()
			solrNoAuthHttp, err := solr.NewSolrHTTP(https, "solrtest")
			Expect(err).To(BeNil())
			err = solrNoAuthClient.Listen()
			Expect(err).To(BeNil())
			replicas, err := locator.GetReplicaUris()
			r, err := solrNoAuthHttp.Select(replicas, solr.FilterQuery("*:*"), solr.Rows(10))
			Expect(err).To(Not(BeNil()))
			Expect(strings.Contains(err.Error(), "401")).To(BeTrue())
			Expect(r.Status).To(BeEquivalentTo(401))
		})

	})

})
<|endoftext|>


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

package main

import ()

type Block struct {
	Template [][]*GridItem
	Offset   Ivec2
}

var (
	OneBlock = Block{
		[][]*GridItem{
			[]*GridItem{
				&GridItem{false, 0, "skeleton01_00"},
			},
		},
		Ivec2{0, 0},
	}

	ThreeBlock = Block{
		[][]*GridItem{
			[]*GridItem{
				&GridItem{false, 0, "special_squares_00"},
				&GridItem{false, 0, "special_squares_00"},
				&GridItem{false, 0, "special_squares_00"},
			},
			[]*GridItem{
				&GridItem{false, 0, "special_squares_00"},
				&GridItem{false, 0, "special_squares_00"},
				&GridItem{false, 0, "special_squares_00"},
			},
			[]*GridItem{
				&GridItem{false, 0, "special_squares_00"},
				&GridItem{false, 0, "special_squares_00"},
				&GridItem{false, 0, "special_squares_00"},
			},
		},
		Ivec2{-1, -1},
	}
)
<commit_msg>Add a range and maximum number of targets for blocks.<commit_after>// Copyright 2015 Pikkpoiss
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import ()

type Block struct {
	Template   [][]*GridItem
	Offset     Ivec2
	Range      float32 // Radius of effectiveness.
	MaxTargets int     // -1 for infinite.
}

var (
	OneBlock = Block{
		[][]*GridItem{
			[]*GridItem{
				&GridItem{false, 0, "skeleton01_00"},
			},
		},
		Ivec2{0, 0},
		1.5,
		1,
	}

	ThreeBlock = Block{
		[][]*GridItem{
			[]*GridItem{
				&GridItem{false, 0, "special_squares_00"},
				&GridItem{false, 0, "special_squares_00"},
				&GridItem{false, 0, "special_squares_00"},
			},
			[]*GridItem{
				&GridItem{false, 0, "special_squares_00"},
				&GridItem{false, 0, "special_squares_00"},
				&GridItem{false, 0, "special_squares_00"},
			},
			[]*GridItem{
				&GridItem{false, 0, "special_squares_00"},
				&GridItem{false, 0, "special_squares_00"},
				&GridItem{false, 0, "special_squares_00"},
			},
		},
		Ivec2{-1, -1},
		5.0,
		3,
	}
)
<|endoftext|>


<commit_before>package main

import (
	"github.com/pinzolo/csvutil"
)

var cmdCombine = &Command{
	Run:       runCombine,
	UsageLine: "combine [OPTIONS...] [FILE]",
	Short:     "列結合",
	Long: `DESCRIPTION
        指定された列を抽出したCSVを出力します。

ARGUMENTS
        FILE
            ソースとなる CSV ファイルのパスを指定します。
            パスが指定されていない場合、標準入力が対象となりパイプでの使用ができます。

OPTIONS
        -w, --overwrite
            指定されたCSVファイルを実行結果で上書きします。
            ファイルパスが渡されていない場合には無視されます。

        -H, --no-header
            ソースとなるCSVの1行目をヘッダー列として扱いません。

        -b, --backup
            処理が成功した場合に、指定されたCSVファイルをバックアップします。
            --overwrite オプションと同時に使用されることを想定しているため、ファイルパスが渡されていない場合には無視されます。

        -e, --encoding ENCODING
            ソースとなるCSVの文字エンコーディングを指定します。
            このオプションが指定されていない場合、csvutil はUTF-8とみなして処理を行います。
            UTF-8であった場合、BOMのあるなしは自動的に判別されます。
            対応している値:
                sjis : Shift_JISとして扱います
                eucjp: EUC_JPとして扱います

        -oe, --output-encoding ENCODING
            出力するCSVの文字エンコーディングを指定します。
            このオプションが指定されていない場合 --encoding オプションで指定されたエンコーディングとして出力します。
            対応している値:
                utf8    : UTF-8として出力します（BOMは出力しません）
                utf8bom : UTF-8として出力します（BOMは出力します）
                sjis    : Shift_JISとして出力します
                eucjp   : EUC_JPとして出力します

        -s, --source COLUMN_SYMBOL(S)
            結合元の列のシンボルを指定します。
            列のシンボルとは列のインデックス（0開始）、もしくはヘッダーテキストです。
            --no-header オプションが指定された場合、インデックスしか受け入れません。
            複数列を対象としたい場合は、foo:bar や 1:2のようにコロン区切りで指定して下さい。

        -d, --destination COLUMN_SYMBOL
            結合後の値を入力する列のシンボルを指定します。
            列のシンボルとは列のインデックス（0開始）、もしくはヘッダーテキストです。
            --no-header オプションが指定された場合、インデックスしか受け入れません。

        -dl, --delimiter TEXT
            結合時にデリミタとする文字列を指定します。初期値は空文字です。
	`,
}

type cmdCombineOption struct {
	csvutil.CombineOption
	// Overwrite to source. (default false)
	Overwrite bool
	// Backup source file. (default false)
	Backup bool
	// Source header or column index separated by colon.
	Source string
}

var combineOpt = cmdCombineOption{}

func init() {
	cmdCombine.Flag.BoolVar(&combineOpt.Overwrite, "overwrite", false, "Overwrite to source.")
	cmdCombine.Flag.BoolVar(&combineOpt.Overwrite, "w", false, "Overwrite to source.")
	cmdCombine.Flag.BoolVar(&combineOpt.NoHeader, "no-header", false, "Source file does not have header line.")
	cmdCombine.Flag.BoolVar(&combineOpt.NoHeader, "H", false, "Source file does not have header line.")
	cmdCombine.Flag.BoolVar(&combineOpt.Backup, "backup", false, "Backup source file.")
	cmdCombine.Flag.BoolVar(&combineOpt.Backup, "b", false, "Backup source file.")
	cmdCombine.Flag.StringVar(&combineOpt.Encoding, "encoding", "utf8", "Encoding of source file")
	cmdCombine.Flag.StringVar(&combineOpt.Encoding, "e", "utf8", "Encoding of source file")
	cmdCombine.Flag.StringVar(&combineOpt.OutputEncoding, "output-encoding", "", "Encoding for output")
	cmdCombine.Flag.StringVar(&combineOpt.OutputEncoding, "oe", "", "Encoding for output")
	cmdCombine.Flag.StringVar(&combineOpt.Source, "source", "", "Source column symbol")
	cmdCombine.Flag.StringVar(&combineOpt.Source, "s", "", "Source column symbol")
	cmdCombine.Flag.StringVar(&combineOpt.Destination, "destination", "", "Destination column symbol")
	cmdCombine.Flag.StringVar(&combineOpt.Destination, "d", "", "Destination column symbol")
	cmdCombine.Flag.StringVar(&combineOpt.Delimiter, "delimiter", "", "Delimiter")
	cmdCombine.Flag.StringVar(&combineOpt.Delimiter, "dl", "", "Delimiter")
}

// runCombine executes combine command and return exit code.
func runCombine(args []string) int {
	success := false
	w, wf, r, rf, err := prepare(args, combineOpt.Overwrite)
	if wf != nil {
		defer wf(&success, combineOpt.Backup)
	}
	if rf != nil {
		defer rf()
	}
	if err != nil {
		return handleError(err)
	}

	opt := combineOpt.CombineOption
	opt.SourceSyms = split(combineOpt.Source)
	err = csvutil.Combine(r, w, opt)
	if err != nil {
		return handleError(err)
	}

	success = true
	return 0
}
<commit_msg>refs #36 Update description.<commit_after>package main

import (
	"github.com/pinzolo/csvutil"
)

var cmdCombine = &Command{
	Run:       runCombine,
	UsageLine: "combine [OPTIONS...] [FILE]",
	Short:     "列結合",
	Long: `DESCRIPTION
        指定された列を結合した値を、指定した列に出力します。

ARGUMENTS
        FILE
            ソースとなる CSV ファイルのパスを指定します。
            パスが指定されていない場合、標準入力が対象となりパイプでの使用ができます。

OPTIONS
        -w, --overwrite
            指定されたCSVファイルを実行結果で上書きします。
            ファイルパスが渡されていない場合には無視されます。

        -H, --no-header
            ソースとなるCSVの1行目をヘッダー列として扱いません。

        -b, --backup
            処理が成功した場合に、指定されたCSVファイルをバックアップします。
            --overwrite オプションと同時に使用されることを想定しているため、ファイルパスが渡されていない場合には無視されます。

        -e, --encoding ENCODING
            ソースとなるCSVの文字エンコーディングを指定します。
            このオプションが指定されていない場合、csvutil はUTF-8とみなして処理を行います。
            UTF-8であった場合、BOMのあるなしは自動的に判別されます。
            対応している値:
                sjis : Shift_JISとして扱います
                eucjp: EUC_JPとして扱います

        -oe, --output-encoding ENCODING
            出力するCSVの文字エンコーディングを指定します。
            このオプションが指定されていない場合 --encoding オプションで指定されたエンコーディングとして出力します。
            対応している値:
                utf8    : UTF-8として出力します（BOMは出力しません）
                utf8bom : UTF-8として出力します（BOMは出力します）
                sjis    : Shift_JISとして出力します
                eucjp   : EUC_JPとして出力します

        -s, --source COLUMN_SYMBOL(S)
            結合元の列のシンボルを指定します。
            列のシンボルとは列のインデックス（0開始）、もしくはヘッダーテキストです。
            --no-header オプションが指定された場合、インデックスしか受け入れません。
            複数列を対象としたい場合は、foo:bar や 1:2のようにコロン区切りで指定して下さい。

        -d, --destination COLUMN_SYMBOL
            結合後の値を入力する列のシンボルを指定します。
            列のシンボルとは列のインデックス（0開始）、もしくはヘッダーテキストです。
            --no-header オプションが指定された場合、インデックスしか受け入れません。

        -dl, --delimiter TEXT
            結合時にデリミタとする文字列を指定します。初期値は空文字です。
	`,
}

type cmdCombineOption struct {
	csvutil.CombineOption
	// Overwrite to source. (default false)
	Overwrite bool
	// Backup source file. (default false)
	Backup bool
	// Source header or column index separated by colon.
	Source string
}

var combineOpt = cmdCombineOption{}

func init() {
	cmdCombine.Flag.BoolVar(&combineOpt.Overwrite, "overwrite", false, "Overwrite to source.")
	cmdCombine.Flag.BoolVar(&combineOpt.Overwrite, "w", false, "Overwrite to source.")
	cmdCombine.Flag.BoolVar(&combineOpt.NoHeader, "no-header", false, "Source file does not have header line.")
	cmdCombine.Flag.BoolVar(&combineOpt.NoHeader, "H", false, "Source file does not have header line.")
	cmdCombine.Flag.BoolVar(&combineOpt.Backup, "backup", false, "Backup source file.")
	cmdCombine.Flag.BoolVar(&combineOpt.Backup, "b", false, "Backup source file.")
	cmdCombine.Flag.StringVar(&combineOpt.Encoding, "encoding", "utf8", "Encoding of source file")
	cmdCombine.Flag.StringVar(&combineOpt.Encoding, "e", "utf8", "Encoding of source file")
	cmdCombine.Flag.StringVar(&combineOpt.OutputEncoding, "output-encoding", "", "Encoding for output")
	cmdCombine.Flag.StringVar(&combineOpt.OutputEncoding, "oe", "", "Encoding for output")
	cmdCombine.Flag.StringVar(&combineOpt.Source, "source", "", "Source column symbol")
	cmdCombine.Flag.StringVar(&combineOpt.Source, "s", "", "Source column symbol")
	cmdCombine.Flag.StringVar(&combineOpt.Destination, "destination", "", "Destination column symbol")
	cmdCombine.Flag.StringVar(&combineOpt.Destination, "d", "", "Destination column symbol")
	cmdCombine.Flag.StringVar(&combineOpt.Delimiter, "delimiter", "", "Delimiter")
	cmdCombine.Flag.StringVar(&combineOpt.Delimiter, "dl", "", "Delimiter")
}

// runCombine executes combine command and return exit code.
func runCombine(args []string) int {
	success := false
	w, wf, r, rf, err := prepare(args, combineOpt.Overwrite)
	if wf != nil {
		defer wf(&success, combineOpt.Backup)
	}
	if rf != nil {
		defer rf()
	}
	if err != nil {
		return handleError(err)
	}

	opt := combineOpt.CombineOption
	opt.SourceSyms = split(combineOpt.Source)
	err = csvutil.Combine(r, w, opt)
	if err != nil {
		return handleError(err)
	}

	success = true
	return 0
}
<|endoftext|>


<commit_before>package run

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"

	"github.com/BurntSushi/toml"
)

const logo = `
 8888888           .d888 888                   8888888b.  888888b.
   888            d88P"  888                   888  "Y88b 888  "88b
   888            888    888                   888    888 888  .88P
   888   88888b.  888888 888 888  888 888  888 888    888 8888888K.
   888   888 "88b 888    888 888  888  Y8bd8P' 888    888 888  "Y88b
   888   888  888 888    888 888  888   X88K   888    888 888    888
   888   888  888 888    888 Y88b 888 .d8""8b. 888  .d88P 888   d88P
 8888888 888  888 888    888  "Y88888 888  888 8888888P"  8888888P"

`

// Command represents the command executed by "influxd run".
type Command struct {
	Version string
	Branch  string
	Commit  string

	closing chan struct{}
	Closed  chan struct{}

	Stdin  io.Reader
	Stdout io.Writer
	Stderr io.Writer

	Server *Server
}

// NewCommand return a new instance of Command.
func NewCommand() *Command {
	return &Command{
		closing: make(chan struct{}),
		Closed:  make(chan struct{}),
		Stdin:   os.Stdin,
		Stdout:  os.Stdout,
		Stderr:  os.Stderr,
	}
}

// Run parses the config from args and runs the server.
func (cmd *Command) Run(args ...string) error {
	// Parse the command line flags.
	options, err := cmd.ParseFlags(args...)
	if err != nil {
		return err
	}

	// Print sweet InfluxDB logo.
	fmt.Print(logo)

	// Write the PID file.
	if err := cmd.writePIDFile(options.PIDFile); err != nil {
		return fmt.Errorf("write pid file: %s", err)
	}

	// Set parallelism.
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Parse config
	config, err := cmd.ParseConfig(options.ConfigPath)
	if err != nil {
		return fmt.Errorf("parse config: %s", err)
	}

	// Apply any environment variables on top of the parsed config
	if err := config.ApplyEnvOverrides(); err != nil {
		return fmt.Errorf("apply env config: %v", err)
	}

	// Override config hostname if specified in the command line args.
	if options.Hostname != "" {
		config.Meta.Hostname = options.Hostname
	}

	if options.Join != "" {
		config.Meta.Peers = strings.Split(options.Join, ",")
	}

	// Validate the configuration.
	if err := config.Validate(); err != nil {
		return fmt.Errorf("%s. To generate a valid configuration file run `influxd config > influxdb.generated.conf`.", err)
	}

	// Create server from config and start it.
	s, err := NewServer(config, cmd.Version)
	if err != nil {
		return fmt.Errorf("create server: %s", err)
	}
	s.CPUProfile = options.CPUProfile
	s.MemProfile = options.MemProfile
	if err := s.Open(); err != nil {
		return fmt.Errorf("open server: %s", err)
	}
	cmd.Server = s

	// Mark start-up in log.
	log.Printf("InfluxDB starting, version %s, branch %s, commit %s", cmd.Version, cmd.Branch, cmd.Commit)
	log.Println("GOMAXPROCS set to", runtime.GOMAXPROCS(0))

	// Begin monitoring the server's error channel.
	go cmd.monitorServerErrors()

	return nil
}

// Close shuts down the server.
func (cmd *Command) Close() error {
	defer close(cmd.Closed)
	close(cmd.closing)
	if cmd.Server != nil {
		return cmd.Server.Close()
	}
	return nil
}

func (cmd *Command) monitorServerErrors() {
	logger := log.New(cmd.Stderr, "", log.LstdFlags)
	for {
		select {
		case err := <-cmd.Server.Err():
			logger.Println(err)
		case <-cmd.closing:
			return
		}
	}
}

// ParseFlags parses the command line flags from args and returns an options set.
func (cmd *Command) ParseFlags(args ...string) (Options, error) {
	var options Options
	fs := flag.NewFlagSet("", flag.ContinueOnError)
	fs.StringVar(&options.ConfigPath, "config", "", "")
	fs.StringVar(&options.PIDFile, "pidfile", "", "")
	fs.StringVar(&options.Hostname, "hostname", "", "")
	fs.StringVar(&options.Join, "join", "", "")
	fs.StringVar(&options.CPUProfile, "cpuprofile", "", "")
	fs.StringVar(&options.MemProfile, "memprofile", "", "")
	fs.Usage = func() { fmt.Fprintln(cmd.Stderr, usage) }
	if err := fs.Parse(args); err != nil {
		return Options{}, err
	}
	return options, nil
}

// writePIDFile writes the process ID to path.
func (cmd *Command) writePIDFile(path string) error {
	// Ignore if path is not set.
	if path == "" {
		return nil
	}

	// Ensure the required directory structure exists.
	err := os.MkdirAll(filepath.Dir(path), 0777)
	if err != nil {
		return fmt.Errorf("mkdir: %s", err)
	}

	// Retrieve the PID and write it.
	pid := strconv.Itoa(os.Getpid())
	if err := ioutil.WriteFile(path, []byte(pid), 0666); err != nil {
		return fmt.Errorf("write file: %s", err)
	}

	return nil
}

// ParseConfig parses the config at path.
// Returns a demo configuration if path is blank.
func (cmd *Command) ParseConfig(path string) (*Config, error) {
	// Use demo configuration if no config path is specified.
	if path == "" {
		fmt.Fprintln(cmd.Stdout, "no configuration provided, using default settings")
		return NewDemoConfig()
	}

	fmt.Fprintf(cmd.Stdout, "Using configuration at: %s\n", path)

	config := NewConfig()
	if _, err := toml.DecodeFile(path, &config); err != nil {
		return nil, err
	}

	return config, nil
}

var usage = `usage: run [flags]

run starts the broker and data node server. If this is the first time running
the command then a new cluster will be initialized unless the -join argument
is used.

        -config <path>
                          Set the path to the configuration file.

        -hostname <name>
                          Override the hostname, the 'hostname' configuration
                          option will be overridden.

        -join <url>
                          Joins the server to an existing cluster.

        -pidfile <path>
                          Write process ID to a file.
`

// Options represents the command line options that can be parsed.
type Options struct {
	ConfigPath string
	PIDFile    string
	Hostname   string
	Join       string
	CPUProfile string
	MemProfile string
}
<commit_msg>turn on block profiling<commit_after>package run

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
)

const logo = `
 8888888           .d888 888                   8888888b.  888888b.
   888            d88P"  888                   888  "Y88b 888  "88b
   888            888    888                   888    888 888  .88P
   888   88888b.  888888 888 888  888 888  888 888    888 8888888K.
   888   888 "88b 888    888 888  888  Y8bd8P' 888    888 888  "Y88b
   888   888  888 888    888 888  888   X88K   888    888 888    888
   888   888  888 888    888 Y88b 888 .d8""8b. 888  .d88P 888   d88P
 8888888 888  888 888    888  "Y88888 888  888 8888888P"  8888888P"

`

// Command represents the command executed by "influxd run".
type Command struct {
	Version string
	Branch  string
	Commit  string

	closing chan struct{}
	Closed  chan struct{}

	Stdin  io.Reader
	Stdout io.Writer
	Stderr io.Writer

	Server *Server
}

// NewCommand return a new instance of Command.
func NewCommand() *Command {
	return &Command{
		closing: make(chan struct{}),
		Closed:  make(chan struct{}),
		Stdin:   os.Stdin,
		Stdout:  os.Stdout,
		Stderr:  os.Stderr,
	}
}

// Run parses the config from args and runs the server.
func (cmd *Command) Run(args ...string) error {
	// Parse the command line flags.
	options, err := cmd.ParseFlags(args...)
	if err != nil {
		return err
	}

	// Print sweet InfluxDB logo.
	fmt.Print(logo)

	// Write the PID file.
	if err := cmd.writePIDFile(options.PIDFile); err != nil {
		return fmt.Errorf("write pid file: %s", err)
	}

	// Set parallelism.
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Turn on block profiling to debug stuck databases
	runtime.SetBlockProfileRate(int(10 * time.Second))

	// Parse config
	config, err := cmd.ParseConfig(options.ConfigPath)
	if err != nil {
		return fmt.Errorf("parse config: %s", err)
	}

	// Apply any environment variables on top of the parsed config
	if err := config.ApplyEnvOverrides(); err != nil {
		return fmt.Errorf("apply env config: %v", err)
	}

	// Override config hostname if specified in the command line args.
	if options.Hostname != "" {
		config.Meta.Hostname = options.Hostname
	}

	if options.Join != "" {
		config.Meta.Peers = strings.Split(options.Join, ",")
	}

	// Validate the configuration.
	if err := config.Validate(); err != nil {
		return fmt.Errorf("%s. To generate a valid configuration file run `influxd config > influxdb.generated.conf`.", err)
	}

	// Create server from config and start it.
	s, err := NewServer(config, cmd.Version)
	if err != nil {
		return fmt.Errorf("create server: %s", err)
	}
	s.CPUProfile = options.CPUProfile
	s.MemProfile = options.MemProfile
	if err := s.Open(); err != nil {
		return fmt.Errorf("open server: %s", err)
	}
	cmd.Server = s

	// Mark start-up in log.
	log.Printf("InfluxDB starting, version %s, branch %s, commit %s", cmd.Version, cmd.Branch, cmd.Commit)
	log.Println("GOMAXPROCS set to", runtime.GOMAXPROCS(0))

	// Begin monitoring the server's error channel.
	go cmd.monitorServerErrors()

	return nil
}

// Close shuts down the server.
func (cmd *Command) Close() error {
	defer close(cmd.Closed)
	close(cmd.closing)
	if cmd.Server != nil {
		return cmd.Server.Close()
	}
	return nil
}

func (cmd *Command) monitorServerErrors() {
	logger := log.New(cmd.Stderr, "", log.LstdFlags)
	for {
		select {
		case err := <-cmd.Server.Err():
			logger.Println(err)
		case <-cmd.closing:
			return
		}
	}
}

// ParseFlags parses the command line flags from args and returns an options set.
func (cmd *Command) ParseFlags(args ...string) (Options, error) {
	var options Options
	fs := flag.NewFlagSet("", flag.ContinueOnError)
	fs.StringVar(&options.ConfigPath, "config", "", "")
	fs.StringVar(&options.PIDFile, "pidfile", "", "")
	fs.StringVar(&options.Hostname, "hostname", "", "")
	fs.StringVar(&options.Join, "join", "", "")
	fs.StringVar(&options.CPUProfile, "cpuprofile", "", "")
	fs.StringVar(&options.MemProfile, "memprofile", "", "")
	fs.Usage = func() { fmt.Fprintln(cmd.Stderr, usage) }
	if err := fs.Parse(args); err != nil {
		return Options{}, err
	}
	return options, nil
}

// writePIDFile writes the process ID to path.
func (cmd *Command) writePIDFile(path string) error {
	// Ignore if path is not set.
	if path == "" {
		return nil
	}

	// Ensure the required directory structure exists.
	err := os.MkdirAll(filepath.Dir(path), 0777)
	if err != nil {
		return fmt.Errorf("mkdir: %s", err)
	}

	// Retrieve the PID and write it.
	pid := strconv.Itoa(os.Getpid())
	if err := ioutil.WriteFile(path, []byte(pid), 0666); err != nil {
		return fmt.Errorf("write file: %s", err)
	}

	return nil
}

// ParseConfig parses the config at path.
// Returns a demo configuration if path is blank.
func (cmd *Command) ParseConfig(path string) (*Config, error) {
	// Use demo configuration if no config path is specified.
	if path == "" {
		fmt.Fprintln(cmd.Stdout, "no configuration provided, using default settings")
		return NewDemoConfig()
	}

	fmt.Fprintf(cmd.Stdout, "Using configuration at: %s\n", path)

	config := NewConfig()
	if _, err := toml.DecodeFile(path, &config); err != nil {
		return nil, err
	}

	return config, nil
}

var usage = `usage: run [flags]

run starts the broker and data node server. If this is the first time running
the command then a new cluster will be initialized unless the -join argument
is used.

        -config <path>
                          Set the path to the configuration file.

        -hostname <name>
                          Override the hostname, the 'hostname' configuration
                          option will be overridden.

        -join <url>
                          Joins the server to an existing cluster.

        -pidfile <path>
                          Write process ID to a file.
`

// Options represents the command line options that can be parsed.
type Options struct {
	ConfigPath string
	PIDFile    string
	Hostname   string
	Join       string
	CPUProfile string
	MemProfile string
}
<|endoftext|>