<commit_before>/*
 * Copyright 2014 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
 */

package vmwarefusion

import (
	"archive/tar"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"runtime"
	"strings"
	"text/template"
	"time"

	"github.com/codegangsta/cli"
	"github.com/docker/machine/drivers"
	"github.com/docker/machine/log"
	"github.com/docker/machine/ssh"
	"github.com/docker/machine/state"
	"github.com/docker/machine/utils"
)

const (
	B2DUser     = "docker"
	B2DPass     = "tcuser"
	isoFilename = "boot2docker.iso"
)

// Driver for VMware Fusion
type Driver struct {
	*drivers.BaseDriver
	Memory         int
	DiskSize       int
	CPU            int
	ISO            string
	Boot2DockerURL string
	CPUS           int
}

func init() {
	drivers.Register("vmwarefusion", &drivers.RegisteredDriver{
		New:            NewDriver,
		GetCreateFlags: GetCreateFlags,
	})
}

// GetCreateFlags registers the flags this driver adds to
// "docker hosts create"
func GetCreateFlags() []cli.Flag {
	return []cli.Flag{
		cli.StringFlag{
			EnvVar: "FUSION_BOOT2DOCKER_URL",
			Name:   "vmwarefusion-boot2docker-url",
			Usage:  "Fusion URL for boot2docker image",
		},
		cli.IntFlag{
			EnvVar: "FUSION_CPU_COUNT",
			Name:   "vmwarefusion-cpu-count",
			Usage:  "number of CPUs for the machine (-1 to use the number of CPUs available)",
			Value:  1,
		},
		cli.IntFlag{
			EnvVar: "FUSION_MEMORY_SIZE",
			Name:   "vmwarefusion-memory-size",
			Usage:  "Fusion size of memory for host VM (in MB)",
			Value:  1024,
		},
		cli.IntFlag{
			EnvVar: "FUSION_DISK_SIZE",
			Name:   "vmwarefusion-disk-size",
			Usage:  "Fusion size of disk for host VM (in MB)",
			Value:  20000,
		},
	}
}

func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) {
	inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey)
	return &Driver{BaseDriver: inner}, nil
}

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

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

	return d.SSHUser
}

func (d *Driver) DriverName() string {
	return "vmwarefusion"
}

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
	d.Memory = flags.Int("vmwarefusion-memory-size")
	d.CPU = flags.Int("vmwarefusion-cpu-count")
	d.DiskSize = flags.Int("vmwarefusion-disk-size")
	d.Boot2DockerURL = flags.String("vmwarefusion-boot2docker-url")
	d.ISO = d.ResolveStorePath(isoFilename)
	d.SwarmMaster = flags.Bool("swarm-master")
	d.SwarmHost = flags.String("swarm-host")
	d.SwarmDiscovery = flags.String("swarm-discovery")
	d.SSHUser = "docker"
	d.SSHPort = 22

	// We support a maximum of 16 cpu to be consistent with Virtual Hardware 10
	// specs.
	if d.CPU > 16 {
		d.CPU = 16
	}

	return nil
}

func (d *Driver) GetURL() (string, error) {
	ip, err := d.GetIP()
	if err != nil {
		return "", err
	}
	if ip == "" {
		return "", nil
	}
	return fmt.Sprintf("tcp://%s:2376", ip), nil
}

func (d *Driver) GetIP() (string, error) {
	s, err := d.GetState()
	if err != nil {
		return "", err
	}
	if s != state.Running {
		return "", drivers.ErrHostIsNotRunning
	}

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

	return ip, nil
}

func (d *Driver) GetState() (state.State, error) {
	// VMRUN only tells use if the vm is running or not
	if stdout, _, _ := vmrun("list"); strings.Contains(stdout, d.vmxPath()) {
		return state.Running, nil
	}
	return state.Stopped, nil
}

func (d *Driver) PreCreateCheck() error {
	return nil
}

func (d *Driver) Create() error {

	b2dutils := utils.NewB2dUtils("", "")
	if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil {
		return err
	}

	log.Infof("Creating SSH key...")
	if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
		return err
	}

	log.Infof("Creating VM...")
	if err := os.MkdirAll(d.ResolveStorePath("."), 0755); err != nil {
		return err
	}

	if _, err := os.Stat(d.vmxPath()); err == nil {
		return ErrMachineExist
	}

	// Generate vmx config file from template
	vmxt := template.Must(template.New("vmx").Parse(vmx))
	vmxfile, err := os.Create(d.vmxPath())
	if err != nil {
		return err
	}
	vmxt.Execute(vmxfile, d)

	// Generate vmdk file
	diskImg := d.ResolveStorePath(fmt.Sprintf("%s.vmdk", d.MachineName))
	if _, err := os.Stat(diskImg); err != nil {
		if !os.IsNotExist(err) {
			return err
		}

		if err := vdiskmanager(diskImg, d.DiskSize); err != nil {
			return err
		}
	}

	log.Infof("Starting %s...", d.MachineName)
	vmrun("start", d.vmxPath(), "nogui")

	var ip string

	log.Infof("Waiting for VM to come online...")
	for i := 1; i <= 60; i++ {
		ip, err = d.getIPfromDHCPLease()
		if err != nil {
			log.Debugf("Not there yet %d/%d, error: %s", i, 60, err)
			time.Sleep(2 * time.Second)
			continue
		}

		if ip != "" {
			log.Debugf("Got an ip: %s", ip)
			break
		}
	}

	if ip == "" {
		return fmt.Errorf("Machine didn't return an IP after 120 seconds, aborting")
	}

	// we got an IP, let's copy ssh keys over
	d.IPAddress = ip

	// Generate a tar keys bundle
	if err := d.generateKeyBundle(); err != nil {
		return err
	}

	// Test if /var/lib/boot2docker exists
	vmrun("-gu", B2DUser, "-gp", B2DPass, "directoryExistsInGuest", d.vmxPath(), "/var/lib/boot2docker")

	// Copy SSH keys bundle
	vmrun("-gu", B2DUser, "-gp", B2DPass, "CopyFileFromHostToGuest", d.vmxPath(), d.ResolveStorePath("userdata.tar"), "/home/docker/userdata.tar")

	// Expand tar file.
	vmrun("-gu", B2DUser, "-gp", B2DPass, "runScriptInGuest", d.vmxPath(), "/bin/sh", "sudo /bin/mv /home/docker/userdata.tar /var/lib/boot2docker/userdata.tar && sudo tar xf /var/lib/boot2docker/userdata.tar -C /home/docker/ > /var/log/userdata.log 2>&1 && sudo chown -R docker:staff /home/docker")

	// Enable Shared Folders
	vmrun("-gu", B2DUser, "-gp", B2DPass, "enableSharedFolders", d.vmxPath())

	var shareName, shareDir string // TODO configurable at some point
	switch runtime.GOOS {
	case "darwin":
		shareName = "Users"
		shareDir = "/Users"
		// TODO "linux" and "windows"
	}

	if shareDir != "" {
		if _, err := os.Stat(shareDir); err != nil && !os.IsNotExist(err) {
			return err
		} else if !os.IsNotExist(err) {
			// add shared folder, create mountpoint and mount it.
			vmrun("-gu", B2DUser, "-gp", B2DPass, "addSharedFolder", d.vmxPath(), shareName, shareDir)
			vmrun("-gu", B2DUser, "-gp", B2DPass, "runScriptInGuest", d.vmxPath(), "/bin/sh", "sudo mkdir "+shareDir+" && sudo mount -t vmhgfs .host:/"+shareName+" "+shareDir)
		}
	}
	return nil
}

func (d *Driver) Start() error {
	log.Infof("Starting %s...", d.MachineName)
	vmrun("start", d.vmxPath(), "nogui")

	log.Debugf("Mounting Shared Folders...")
	var shareName, shareDir string // TODO configurable at some point
	switch runtime.GOOS {
	case "darwin":
		shareName = "Users"
		shareDir = "/Users"
		// TODO "linux" and "windows"
	}

	if shareDir != "" {
		if _, err := os.Stat(shareDir); err != nil && !os.IsNotExist(err) {
			return err
		} else if !os.IsNotExist(err) {
			// create mountpoint and mount shared folder
			vmrun("-gu", B2DUser, "-gp", B2DPass, "runScriptInGuest", d.vmxPath(), "/bin/sh", "sudo mkdir "+shareDir+" && sudo mount -t vmhgfs .host:/"+shareName+" "+shareDir)
		}
	}

	return nil
}

func (d *Driver) Stop() error {
	log.Infof("Gracefully shutting down %s...", d.MachineName)
	vmrun("stop", d.vmxPath(), "nogui")
	return nil
}

func (d *Driver) Remove() error {

	s, _ := d.GetState()
	if s == state.Running {
		if err := d.Kill(); err != nil {
			return fmt.Errorf("Error stopping VM before deletion")
		}
	}
	log.Infof("Deleting %s...", d.MachineName)
	vmrun("deleteVM", d.vmxPath(), "nogui")
	return nil
}

func (d *Driver) Restart() error {
	log.Infof("Gracefully restarting %s...", d.MachineName)
	vmrun("reset", d.vmxPath(), "nogui")
	return nil
}

func (d *Driver) Kill() error {
	log.Infof("Forcibly halting %s...", d.MachineName)
	vmrun("stop", d.vmxPath(), "hard nogui")
	return nil
}

func (d *Driver) Upgrade() error {
	return fmt.Errorf("VMware Fusion does not currently support the upgrade operation")
}

func (d *Driver) vmxPath() string {
	return d.ResolveStorePath(fmt.Sprintf("%s.vmx", d.MachineName))
}

func (d *Driver) vmdkPath() string {
	return d.ResolveStorePath(fmt.Sprintf("%s.vmdk", d.MachineName))
}

func (d *Driver) getIPfromDHCPLease() (string, error) {
	var vmxfh *os.File
	var dhcpfh *os.File
	var vmxcontent []byte
	var dhcpcontent []byte
	var macaddr string
	var err error
	var lastipmatch string
	var currentip string
	var lastleaseendtime time.Time
	var currentleadeendtime time.Time

	// DHCP lease table for NAT vmnet interface
	var dhcpfile = "/var/db/vmware/vmnet-dhcpd-vmnet8.leases"

	if vmxfh, err = os.Open(d.vmxPath()); err != nil {
		return "", err
	}
	defer vmxfh.Close()

	if vmxcontent, err = ioutil.ReadAll(vmxfh); err != nil {
		return "", err
	}

	// Look for generatedAddress as we're passing a VMX with addressType = "generated".
	vmxparse := regexp.MustCompile(`^ethernet0.generatedAddress\s*=\s*"(.*?)"\s*$`)
	for _, line := range strings.Split(string(vmxcontent), "\n") {
		if matches := vmxparse.FindStringSubmatch(line); matches == nil {
			continue
		} else {
			macaddr = strings.ToLower(matches[1])
		}
	}

	if macaddr == "" {
		return "", fmt.Errorf("couldn't find MAC address in VMX file %s", d.vmxPath())
	}

	log.Debugf("MAC address in VMX: %s", macaddr)
	if dhcpfh, err = os.Open(dhcpfile); err != nil {
		return "", err
	}
	defer dhcpfh.Close()

	if dhcpcontent, err = ioutil.ReadAll(dhcpfh); err != nil {
		return "", err
	}

	// Get the IP from the lease table.
	leaseip := regexp.MustCompile(`^lease (.+?) {$`)
	// Get the lease end date time.
	leaseend := regexp.MustCompile(`^\s*ends \d (.+?);$`)
	// Get the MAC address associated.
	leasemac := regexp.MustCompile(`^\s*hardware ethernet (.+?);$`)

	for _, line := range strings.Split(string(dhcpcontent), "\n") {

		if matches := leaseip.FindStringSubmatch(line); matches != nil {
			lastipmatch = matches[1]
			continue
		}

		if matches := leaseend.FindStringSubmatch(line); matches != nil {
			lastleaseendtime, _ = time.Parse("2006/01/02 15:04:05", matches[1])
			continue
		}

		if matches := leasemac.FindStringSubmatch(line); matches != nil && matches[1] == macaddr && currentleadeendtime.Before(lastleaseendtime) {
			currentip = lastipmatch
			currentleadeendtime = lastleaseendtime
		}
	}

	if currentip == "" {
		return "", fmt.Errorf("IP not found for MAC %s in DHCP leases", macaddr)
	}

	log.Debugf("IP found in DHCP lease table: %s", currentip)
	return currentip, nil

}

func (d *Driver) publicSSHKeyPath() string {
	return d.GetSSHKeyPath() + ".pub"
}

// Make a boot2docker userdata.tar key bundle
func (d *Driver) generateKeyBundle() error {
	log.Debugf("Creating Tar key bundle...")

	magicString := "boot2docker, this is vmware speaking"

	tf, err := os.Create(d.ResolveStorePath("userdata.tar"))
	if err != nil {
		return err
	}
	defer tf.Close()
	var fileWriter = tf

	tw := tar.NewWriter(fileWriter)
	defer tw.Close()

	// magicString first so we can figure out who originally wrote the tar.
	file := &tar.Header{Name: magicString, Size: int64(len(magicString))}
	if err := tw.WriteHeader(file); err != nil {
		return err
	}
	if _, err := tw.Write([]byte(magicString)); err != nil {
		return err
	}
	// .ssh/key.pub => authorized_keys
	file = &tar.Header{Name: ".ssh", Typeflag: tar.TypeDir, Mode: 0700}
	if err := tw.WriteHeader(file); err != nil {
		return err
	}
	pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath())
	if err != nil {
		return err
	}
	file = &tar.Header{Name: ".ssh/authorized_keys", Size: int64(len(pubKey)), Mode: 0644}
	if err := tw.WriteHeader(file); err != nil {
		return err
	}
	if _, err := tw.Write([]byte(pubKey)); err != nil {
		return err
	}
	file = &tar.Header{Name: ".ssh/authorized_keys2", Size: int64(len(pubKey)), Mode: 0644}
	if err := tw.WriteHeader(file); err != nil {
		return err
	}
	if _, err := tw.Write([]byte(pubKey)); err != nil {
		return err
	}
	if err := tw.Close(); err != nil {
		return err
	}

	return nil

}
<commit_msg>fusion: update for b2d-ng<commit_after>/*
 * Copyright 2014 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
 */

package vmwarefusion

import (
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
	"text/template"
	"time"

	"github.com/codegangsta/cli"
	"github.com/docker/docker/pkg/homedir"
	"github.com/docker/machine/drivers"
	"github.com/docker/machine/log"
	"github.com/docker/machine/ssh"
	"github.com/docker/machine/state"
	"github.com/docker/machine/utils"
)

const (
	B2DUser     = "docker"
	B2DPass     = "docker"
	isoFilename = "boot2docker.iso"
)

// Driver for VMware Fusion
type Driver struct {
	*drivers.BaseDriver
	Memory         int
	DiskSize       int
	CPU            int
	ISO            string
	Boot2DockerURL string
	CPUS           int
}

func init() {
	drivers.Register("vmwarefusion", &drivers.RegisteredDriver{
		New:            NewDriver,
		GetCreateFlags: GetCreateFlags,
	})
}

// GetCreateFlags registers the flags this driver adds to
// "docker hosts create"
func GetCreateFlags() []cli.Flag {
	return []cli.Flag{
		cli.StringFlag{
			EnvVar: "FUSION_BOOT2DOCKER_URL",
			Name:   "vmwarefusion-boot2docker-url",
			Usage:  "Fusion URL for boot2docker image",
		},
		cli.IntFlag{
			EnvVar: "FUSION_CPU_COUNT",
			Name:   "vmwarefusion-cpu-count",
			Usage:  "number of CPUs for the machine (-1 to use the number of CPUs available)",
			Value:  1,
		},
		cli.IntFlag{
			EnvVar: "FUSION_MEMORY_SIZE",
			Name:   "vmwarefusion-memory-size",
			Usage:  "Fusion size of memory for host VM (in MB)",
			Value:  1024,
		},
		cli.IntFlag{
			EnvVar: "FUSION_DISK_SIZE",
			Name:   "vmwarefusion-disk-size",
			Usage:  "Fusion size of disk for host VM (in MB)",
			Value:  20000,
		},
	}
}

func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) {
	inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey)
	return &Driver{BaseDriver: inner}, nil
}

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

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

	return d.SSHUser
}

func (d *Driver) DriverName() string {
	return "vmwarefusion"
}

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
	d.Memory = flags.Int("vmwarefusion-memory-size")
	d.CPU = flags.Int("vmwarefusion-cpu-count")
	d.DiskSize = flags.Int("vmwarefusion-disk-size")
	d.Boot2DockerURL = flags.String("vmwarefusion-boot2docker-url")
	d.ISO = d.ResolveStorePath(isoFilename)
	d.SwarmMaster = flags.Bool("swarm-master")
	d.SwarmHost = flags.String("swarm-host")
	d.SwarmDiscovery = flags.String("swarm-discovery")
	d.SSHUser = "docker"
	d.SSHPort = 22

	// We support a maximum of 16 cpu to be consistent with Virtual Hardware 10
	// specs.
	if d.CPU > 16 {
		d.CPU = 16
	}

	return nil
}

func (d *Driver) GetURL() (string, error) {
	ip, err := d.GetIP()
	if err != nil {
		return "", err
	}
	if ip == "" {
		return "", nil
	}
	return fmt.Sprintf("tcp://%s:2376", ip), nil
}

func (d *Driver) GetIP() (string, error) {
	s, err := d.GetState()
	if err != nil {
		return "", err
	}
	if s != state.Running {
		return "", drivers.ErrHostIsNotRunning
	}

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

	return ip, nil
}

func (d *Driver) GetState() (state.State, error) {
	// VMRUN only tells use if the vm is running or not
	if stdout, _, _ := vmrun("list"); strings.Contains(stdout, d.vmxPath()) {
		return state.Running, nil
	}
	return state.Stopped, nil
}

func (d *Driver) PreCreateCheck() error {
	return nil
}

func (d *Driver) Create() error {

	b2dutils := utils.NewB2dUtils("", "")
	if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil {
		return err
	}

	log.Infof("Creating SSH key...")
	if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
		return err
	}

	log.Infof("Creating VM...")
	if err := os.MkdirAll(d.ResolveStorePath("."), 0755); err != nil {
		return err
	}

	if _, err := os.Stat(d.vmxPath()); err == nil {
		return ErrMachineExist
	}

	// Generate vmx config file from template
	vmxt := template.Must(template.New("vmx").Parse(vmx))
	vmxfile, err := os.Create(d.vmxPath())
	if err != nil {
		return err
	}
	vmxt.Execute(vmxfile, d)

	// Generate vmdk file
	diskImg := d.ResolveStorePath(fmt.Sprintf("%s.vmdk", d.MachineName))
	if _, err := os.Stat(diskImg); err != nil {
		if !os.IsNotExist(err) {
			return err
		}

		if err := vdiskmanager(diskImg, d.DiskSize); err != nil {
			return err
		}
	}

	log.Infof("Starting %s...", d.MachineName)
	vmrun("start", d.vmxPath(), "nogui")

	var ip string

	log.Infof("Waiting for VM to come online...")
	for i := 1; i <= 60; i++ {
		ip, err = d.GetIP()
		if err != nil {
			log.Debugf("Not there yet %d/%d, error: %s", i, 60, err)
			time.Sleep(2 * time.Second)
			continue
		}

		if ip != "" {
			log.Debugf("Got an ip: %s", ip)
			break
		}
	}

	if ip == "" {
		return fmt.Errorf("Machine didn't return an IP after 120 seconds, aborting")
	}

	// we got an IP, let's copy ssh keys over
	d.IPAddress = ip

	// use ssh to set keys
	sshClient, err := d.getLocalSSHClient()
	if err != nil {
		return err
	}

	// add pub key for user
	pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath())
	if err != nil {
		return err
	}

	if out, err := sshClient.Output(fmt.Sprintf(
		"mkdir -p /home/%s/.ssh",
		d.GetSSHUsername(),
	)); err != nil {
		log.Error(out)
		return err
	}

	if out, err := sshClient.Output(fmt.Sprintf(
		"printf '%%s' '%s' | tee /home/%s/.ssh/authorized_keys",
		string(pubKey),
		d.GetSSHUsername(),
	)); err != nil {
		log.Error(out)
		return err
	}

	// Enable Shared Folders
	vmrun("-gu", B2DUser, "-gp", B2DPass, "enableSharedFolders", d.vmxPath())

	if err := d.setupSharedDirs(); err != nil {
		return err
	}

	return nil
}

func (d *Driver) Start() error {
	log.Infof("Starting %s...", d.MachineName)
	vmrun("start", d.vmxPath(), "nogui")

	log.Debugf("Mounting Shared Folders...")
	if err := d.setupSharedDirs(); err != nil {
		return err
	}

	return nil
}

func (d *Driver) Stop() error {
	log.Infof("Gracefully shutting down %s...", d.MachineName)
	vmrun("stop", d.vmxPath(), "nogui")
	return nil
}

func (d *Driver) Remove() error {

	s, _ := d.GetState()
	if s == state.Running {
		if err := d.Kill(); err != nil {
			return fmt.Errorf("Error stopping VM before deletion")
		}
	}
	log.Infof("Deleting %s...", d.MachineName)
	vmrun("deleteVM", d.vmxPath(), "nogui")
	return nil
}

func (d *Driver) Restart() error {
	log.Infof("Gracefully restarting %s...", d.MachineName)
	vmrun("reset", d.vmxPath(), "nogui")
	return nil
}

func (d *Driver) Kill() error {
	log.Infof("Forcibly halting %s...", d.MachineName)
	vmrun("stop", d.vmxPath(), "hard nogui")
	return nil
}

func (d *Driver) Upgrade() error {
	return fmt.Errorf("VMware Fusion does not currently support the upgrade operation")
}

func (d *Driver) vmxPath() string {
	return d.ResolveStorePath(fmt.Sprintf("%s.vmx", d.MachineName))
}

func (d *Driver) vmdkPath() string {
	return d.ResolveStorePath(fmt.Sprintf("%s.vmdk", d.MachineName))
}

func (d *Driver) getIPfromDHCPLease() (string, error) {
	var vmxfh *os.File
	var dhcpfh *os.File
	var vmxcontent []byte
	var dhcpcontent []byte
	var macaddr string
	var err error
	var lastipmatch string
	var currentip string
	var lastleaseendtime time.Time
	var currentleadeendtime time.Time

	// DHCP lease table for NAT vmnet interface
	var dhcpfile = "/var/db/vmware/vmnet-dhcpd-vmnet8.leases"

	if vmxfh, err = os.Open(d.vmxPath()); err != nil {
		return "", err
	}
	defer vmxfh.Close()

	if vmxcontent, err = ioutil.ReadAll(vmxfh); err != nil {
		return "", err
	}

	// Look for generatedAddress as we're passing a VMX with addressType = "generated".
	vmxparse := regexp.MustCompile(`^ethernet0.generatedAddress\s*=\s*"(.*?)"\s*$`)
	for _, line := range strings.Split(string(vmxcontent), "\n") {
		if matches := vmxparse.FindStringSubmatch(line); matches == nil {
			continue
		} else {
			macaddr = strings.ToLower(matches[1])
		}
	}

	if macaddr == "" {
		return "", fmt.Errorf("couldn't find MAC address in VMX file %s", d.vmxPath())
	}

	log.Debugf("MAC address in VMX: %s", macaddr)
	if dhcpfh, err = os.Open(dhcpfile); err != nil {
		return "", err
	}
	defer dhcpfh.Close()

	if dhcpcontent, err = ioutil.ReadAll(dhcpfh); err != nil {
		return "", err
	}

	// Get the IP from the lease table.
	leaseip := regexp.MustCompile(`^lease (.+?) {$`)
	// Get the lease end date time.
	leaseend := regexp.MustCompile(`^\s*ends \d (.+?);$`)
	// Get the MAC address associated.
	leasemac := regexp.MustCompile(`^\s*hardware ethernet (.+?);$`)

	for _, line := range strings.Split(string(dhcpcontent), "\n") {

		if matches := leaseip.FindStringSubmatch(line); matches != nil {
			lastipmatch = matches[1]
			continue
		}

		if matches := leaseend.FindStringSubmatch(line); matches != nil {
			lastleaseendtime, _ = time.Parse("2006/01/02 15:04:05", matches[1])
			continue
		}

		if matches := leasemac.FindStringSubmatch(line); matches != nil && matches[1] == macaddr && currentleadeendtime.Before(lastleaseendtime) {
			currentip = lastipmatch
			currentleadeendtime = lastleaseendtime
		}
	}

	if currentip == "" {
		return "", fmt.Errorf("IP not found for MAC %s in DHCP leases", macaddr)
	}

	log.Debugf("IP found in DHCP lease table: %s", currentip)
	return currentip, nil

}

func (d *Driver) publicSSHKeyPath() string {
	return d.GetSSHKeyPath() + ".pub"
}

func (d *Driver) setupSharedDirs() error {
	shareDir := homedir.Get()
	shareName := shareDir

	if _, err := os.Stat(shareDir); err != nil && !os.IsNotExist(err) {
		return err
	} else if !os.IsNotExist(err) {
		// add shared folder, create mountpoint and mount it.
		vmrun("-gu", B2DUser, "-gp", B2DPass, "addSharedFolder", d.vmxPath(), shareName, shareDir)
		vmrun("-gu", B2DUser, "-gp", B2DPass, "runScriptInGuest", d.vmxPath(), "/bin/sh", "sudo mkdir "+shareDir+" && sudo mount -t vmhgfs .host:"+shareName+" "+shareDir)
	}

	return nil
}

func (d *Driver) getLocalSSHClient() (ssh.Client, error) {
	ip, err := d.GetIP()
	if err != nil {
		return nil, err
	}

	sshAuth := &ssh.Auth{
		Passwords: []string{"docker"},
		Keys:      []string{d.GetSSHKeyPath()},
	}
	sshClient, err := ssh.NewNativeClient(d.GetSSHUsername(), ip, d.SSHPort, sshAuth)
	if err != nil {
		return nil, err
	}

	return sshClient, nil
}
<|endoftext|>


<commit_before>// Package loggers provides objects that gathers resource data from a host.
package loggers

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log/syslog"
	"net/http"
	"os"
	"reflect"
	"regexp"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/hpcloud/tail"
	"github.com/sethgrid/pester"

	resourced_wire "github.com/resourced/resourced-wire"
	resourced_config "github.com/resourced/resourced/config"
	"github.com/resourced/resourced/host"
	"github.com/resourced/resourced/libmap"
	"github.com/resourced/resourced/libstring"
	"github.com/resourced/resourced/libtcp"
)

var loggerConstructors = make(map[string]func() ILogger)

func init() {
	Register("Base", NewBase)
}

// Register makes any logger constructor available by name.
func Register(name string, constructor func() ILogger) {
	if constructor == nil {
		panic("logger: Register logger constructor is nil")
	}
	if _, dup := loggerConstructors[name]; dup {
		panic("logger: Register called twice for logger constructor " + name)
	}
	loggerConstructors[name] = constructor
}

// NewGoStruct instantiates ILogger
func NewGoStruct(name string) (ILogger, error) {
	constructor, ok := loggerConstructors[name]
	if !ok {
		return nil, errors.New("GoStruct is undefined.")
	}

	return constructor(), nil
}

// NewGoStructByConfig instantiates ILogger given Config struct
func NewGoStructByConfig(config resourced_config.Config) (ILogger, error) {
	lgr, err := NewGoStruct(config.GoStruct)
	if err != nil {
		return nil, err
	}

	lgr.SetSource(config.Source)
	lgr.SetBufferSize(config.BufferSize)
	lgr.SetTargets(config.Targets)

	// Populate ILogger fields dynamically
	if len(config.GoStructFields) > 0 {
		for structFieldInString, value := range config.GoStructFields {
			goStructField := reflect.ValueOf(lgr).Elem().FieldByName(structFieldInString)

			if goStructField.IsValid() && goStructField.CanSet() {
				valueOfValue := reflect.ValueOf(value)
				goStructField.Set(valueOfValue)
			}
		}
	}

	return lgr, err
}

// ILogger is generic interface for all loggers.
type ILogger interface {
	SetSource(string)
	GetSource() string

	SetBufferSize(int64)
	GetBufferSize() int64

	SetTargets([]resourced_config.LogTargetConfig)
	GetTargets() []resourced_config.LogTargetConfig

	SetLoglines(string, []string)
	GetLoglines(string) []string
	GetLoglinesLength(string) int
	GetAndResetLoglines(string) []string
	ResetLoglines(string)
	ProcessOutgoingLoglines([]string, []string, []string) []string

	LogErrorAndResetLoglinesIfNeeded(string, error, string)

	SendToMaster(string, string, string, *host.Host, []string, string) error

	SendToAgent(string, int, []string, string) error

	SendToSyslog(string, string, syslog.Priority, string, []string, string) error

	SendToGenericTCP(string, int, []string, string) error

	WriteToFile(string, []string) error
}

type ILoggerChannel interface {
	ILogger
	PubSubKey(string) string
	RunBlockingChannel(string, <-chan interface{})
}

type ILoggerFile interface {
	ILogger
	RunBlockingFile(string)
}

func NewBase() ILogger {
	b := &Base{}
	b.Data = libmap.NewTSafeMapStrings(nil)
	b.BufferSize = 1000000

	return b
}

type Base struct {
	Source     string
	BufferSize int64
	Targets    []resourced_config.LogTargetConfig

	Data *libmap.TSafeMapStrings
}

// PubSubKey is used for forwarding live log line from TCP listener to every target's channel.
func (b *Base) PubSubKey(targetEndpoint string) string {
	return b.GetSource() + "->" + targetEndpoint
}

// RunBlockingChannel pulls log line from channel continuously.
func (b *Base) RunBlockingChannel(name string, ch <-chan interface{}) {
	for line := range ch {
		b.Data.Append(name, line.(string))
	}
}

// RunBlockingFile tails the file continuously.
func (b *Base) RunBlockingFile(file string) {
	t, err := tail.TailFile(file, tail.Config{
		Follow:   true,
		Location: &tail.SeekInfo{Offset: 0, Whence: os.SEEK_END},
		Logger:   logrus.New(),
	})
	if err == nil {
		if !b.Data.Exists(file) {
			b.Data.Set(file, make([]string, 0))
		}

		for line := range t.Lines {
			b.Data.Append(file, line.Text)
		}
	}
}

// SetSource
func (b *Base) SetSource(source string) {
	b.Source = source
}

// GetSource returns the source field.
func (b *Base) GetSource() string {
	return b.Source
}

// SetBufferSize sets BufferSize
func (b *Base) SetBufferSize(bufferSize int64) {
	b.BufferSize = bufferSize
}

// GetBufferSize returns BufferSize
func (b *Base) GetBufferSize() int64 {
	return b.BufferSize
}

// SetTargets sets []LogTargetConfig
func (b *Base) SetTargets(targets []resourced_config.LogTargetConfig) {
	b.Targets = targets
}

// SetLoglines sets loglines.
func (b *Base) SetLoglines(source string, loglines []string) {
	b.Data.Set(source, loglines)
}

// GetLoglines returns loglines.
func (b *Base) GetLoglines(source string) []string {
	return b.Data.Get(source)
}

// GetLoglinesLength returns the count of loglines.
func (b *Base) GetLoglinesLength(source string) int {
	return len(b.Data.Get(source))
}

// GetAndResetLoglines returns loglines.
func (b *Base) GetAndResetLoglines(source string) []string {
	loglines := b.Data.Get(source)
	b.ResetLoglines(source)
	return loglines
}

// ResetLoglines wipes it clean.
func (b *Base) ResetLoglines(source string) {
	b.Data.Reset(source)
}

// GetTargets returns slice of LogTargetConfig.
func (b *Base) GetTargets() []resourced_config.LogTargetConfig {
	return b.Targets
}

func (b *Base) LogErrorAndResetLoglinesIfNeeded(source string, err error, message string) {
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"Error": err.Error(),
		}).Error(message)

		// Check if we have to prune in-memory log lines.
		if int64(b.GetLoglinesLength(source)) > b.GetBufferSize() {
			b.ResetLoglines(source)
		}
	}
}

// denyLoglines filters out every log line that matches denyList regex.
func (b *Base) denyLoglines(loglines []string, denyList []string) []string {
	newLoglines := make([]string, 0)

	for _, logline := range loglines {
		for _, deny := range denyList {
			if deny == "" {
				newLoglines = append(newLoglines, logline)
				continue
			}

			match, err := regexp.MatchString(deny, logline)
			if err != nil || !match {
				newLoglines = append(newLoglines, logline)
			}
		}
	}

	return newLoglines
}

// allowLoglines filters out every log line that matches allowList regex.
func (b *Base) allowLoglines(loglines []string, allowList []string) []string {
	newLoglines := make([]string, 0)

	for _, logline := range loglines {
		for _, allow := range allowList {
			match, err := regexp.MatchString(allow, logline)
			if err == nil && match {
				newLoglines = append(newLoglines, logline)
			}
		}
	}

	return newLoglines
}

// ProcessOutgoingLoglines before forwarding to targets.
func (b *Base) ProcessOutgoingLoglines(loglines []string, allowList []string, denyList []string) []string {
	loglines = libstring.StitchIndentedInLoglines(loglines)

	if len(allowList) > 0 {
		return b.allowLoglines(loglines, allowList)
	}
	if len(denyList) > 0 {
		return b.denyLoglines(loglines, denyList)
	}

	return loglines
}

// logPayloadForMaster packages the log data before sending to master.
func (b *Base) logPayloadForMaster(hostData *host.Host, loglines []string, source string) AgentLogPayload {
	toSend := AgentLogPayload{}
	toSend.Host.Name = hostData.Name
	toSend.Host.Tags = hostData.Tags
	toSend.Data.Filename = source
	toSend.Data.Loglines = make([]AgentLoglinePayload, 0)

	for _, lg := range loglines {
		linePayload := AgentLoglinePayload{}

		wirePayload := resourced_wire.ParseSingle(lg)

		// Check if loglines contain ResourceD base64 wire protocol.
		// If so, convert to plain text.
		if wirePayload.Type == "base64" {
			lg = resourced_wire.ParseSingle(lg).EncodePlain()
		}

		// Check if each logline is NOT ResourceD log wire protocol
		if wirePayload.Type == "" {
			linePayload.Created = time.Now().UTC().Unix()
			linePayload.Content = wirePayload.EncodePlain()
		} else {
			linePayload.Created = wirePayload.Created
			linePayload.Content = wirePayload.Content
		}

		if linePayload.Content != "" {
			toSend.Data.Loglines = append(toSend.Data.Loglines, linePayload)
		}
	}

	return toSend
}

// SendToMaster sends log lines to master.
func (b *Base) SendToMaster(accessToken, masterURLHost, masterURLPath string, hostData *host.Host, loglines []string, source string) error {
	// Don't do anything if there are no log lines to send.
	if len(loglines) == 0 {
		return nil
	}

	if masterURLPath == "" {
		masterURLPath = "/api/logs"
	}

	data := b.logPayloadForMaster(hostData, loglines, source)

	dataJson, err := json.Marshal(data)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"Error": err.Error(),
		}).Error("Failed to marshal JSON payload for sending logs to ResourceD Master")

		return err
	}

	url := masterURLHost + masterURLPath

	req, err := http.NewRequest("POST", url, bytes.NewBuffer(dataJson))
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"Error": err.Error(),
		}).Error("Failed to create request struct for sending logs to ResourceD Master")

		return err
	}

	req.SetBasicAuth(accessToken, "")

	client := pester.New()
	client.MaxRetries = 3
	client.Backoff = pester.ExponentialJitterBackoff
	client.KeepLog = false

	resp, err := client.Do(req)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"Error":      err.Error(),
			"req.URL":    req.URL.String(),
			"req.Method": req.Method,
		}).Error("Failed to send logs data to ResourceD Master")

		return err
	}

	if resp != nil && resp.Body != nil {
		defer resp.Body.Close()
	}

	return err
}

// SendToAgent sends log lines to another agent.
func (b *Base) SendToAgent(anotherAgentAddr string, maxRetries int, loglines []string, source string) error {
	if len(loglines) == 0 {
		return nil
	}

	conn, err := libtcp.NewConnectionWithRetries(anotherAgentAddr, maxRetries)
	if err != nil {
		return err
	}

	if conn != nil {
		defer conn.Close()

		w := bufio.NewWriter(conn)

		for i, lg := range loglines {
			// Check if each logline is NOT encoded in ResourceD wire protocol
			wire := resourced_wire.ParseSingle(lg)
			if wire.Type == "" {
				wire.Created = time.Now().UTC().Unix()
				wire.Content = lg

				loglines[i] = wire.EncodeBase64()
				lg = loglines[i]
			}

			fmt.Fprint(w, lg)
			w.Flush()
		}
	}

	return err
}

// SendToSyslog sends log lines to syslog endpoint.
func (b *Base) SendToSyslog(protocol string, addr string, priority syslog.Priority, tag string, loglines []string, source string) error {
	if len(loglines) == 0 {
		return nil
	}

	logHandler, err := syslog.Dial(protocol, addr, priority, tag)
	if err != nil {
		return err
	}
	defer logHandler.Close()

	for _, lg := range loglines {
		logHandler.Write([]byte(resourced_wire.ParseSingle(lg).PlainContent()))
	}

	return nil
}

// SendToGenericTCP sends log lines to a generic tcp endpoint.
func (b *Base) SendToGenericTCP(addr string, maxRetries int, loglines []string, source string) error {
	if len(loglines) == 0 {
		return nil
	}

	conn, err := libtcp.NewConnectionWithRetries(addr, maxRetries)
	if err != nil {
		return err
	}

	if conn != nil {
		defer conn.Close()

		w := bufio.NewWriter(conn)

		for _, lg := range loglines {
			fmt.Fprint(w, lg)
			w.Flush()
		}
	}

	return err
}

// WriteToFile writes log lines to local file.
func (b *Base) WriteToFile(targetFile string, loglines []string) error {
	// Check if loglines contain ResourceD base64 wire protocol.
	// If so, convert to plain text.
	for i, lg := range loglines {
		wire := resourced_wire.ParseSingle(lg)
		if wire.Type != "" {
			loglines[i] = wire.PlainContent()
		}
	}

	fileHandle, err := os.OpenFile(targetFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		return err
	}
	defer fileHandle.Close()

	for _, logline := range loglines {
		if strings.HasSuffix(logline, "\n") {
			fileHandle.WriteString(logline)
		} else {
			fileHandle.WriteString(logline + "\n")
		}
	}

	return nil
}
<commit_msg>Fix bug: double encode logline when shipping to master.<commit_after>// Package loggers provides objects that gathers resource data from a host.
package loggers

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log/syslog"
	"net/http"
	"os"
	"reflect"
	"regexp"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/hpcloud/tail"
	"github.com/sethgrid/pester"

	resourced_wire "github.com/resourced/resourced-wire"
	resourced_config "github.com/resourced/resourced/config"
	"github.com/resourced/resourced/host"
	"github.com/resourced/resourced/libmap"
	"github.com/resourced/resourced/libstring"
	"github.com/resourced/resourced/libtcp"
)

var loggerConstructors = make(map[string]func() ILogger)

func init() {
	Register("Base", NewBase)
}

// Register makes any logger constructor available by name.
func Register(name string, constructor func() ILogger) {
	if constructor == nil {
		panic("logger: Register logger constructor is nil")
	}
	if _, dup := loggerConstructors[name]; dup {
		panic("logger: Register called twice for logger constructor " + name)
	}
	loggerConstructors[name] = constructor
}

// NewGoStruct instantiates ILogger
func NewGoStruct(name string) (ILogger, error) {
	constructor, ok := loggerConstructors[name]
	if !ok {
		return nil, errors.New("GoStruct is undefined.")
	}

	return constructor(), nil
}

// NewGoStructByConfig instantiates ILogger given Config struct
func NewGoStructByConfig(config resourced_config.Config) (ILogger, error) {
	lgr, err := NewGoStruct(config.GoStruct)
	if err != nil {
		return nil, err
	}

	lgr.SetSource(config.Source)
	lgr.SetBufferSize(config.BufferSize)
	lgr.SetTargets(config.Targets)

	// Populate ILogger fields dynamically
	if len(config.GoStructFields) > 0 {
		for structFieldInString, value := range config.GoStructFields {
			goStructField := reflect.ValueOf(lgr).Elem().FieldByName(structFieldInString)

			if goStructField.IsValid() && goStructField.CanSet() {
				valueOfValue := reflect.ValueOf(value)
				goStructField.Set(valueOfValue)
			}
		}
	}

	return lgr, err
}

// ILogger is generic interface for all loggers.
type ILogger interface {
	SetSource(string)
	GetSource() string

	SetBufferSize(int64)
	GetBufferSize() int64

	SetTargets([]resourced_config.LogTargetConfig)
	GetTargets() []resourced_config.LogTargetConfig

	SetLoglines(string, []string)
	GetLoglines(string) []string
	GetLoglinesLength(string) int
	GetAndResetLoglines(string) []string
	ResetLoglines(string)
	ProcessOutgoingLoglines([]string, []string, []string) []string

	LogErrorAndResetLoglinesIfNeeded(string, error, string)

	SendToMaster(string, string, string, *host.Host, []string, string) error

	SendToAgent(string, int, []string, string) error

	SendToSyslog(string, string, syslog.Priority, string, []string, string) error

	SendToGenericTCP(string, int, []string, string) error

	WriteToFile(string, []string) error
}

type ILoggerChannel interface {
	ILogger
	PubSubKey(string) string
	RunBlockingChannel(string, <-chan interface{})
}

type ILoggerFile interface {
	ILogger
	RunBlockingFile(string)
}

func NewBase() ILogger {
	b := &Base{}
	b.Data = libmap.NewTSafeMapStrings(nil)
	b.BufferSize = 1000000

	return b
}

type Base struct {
	Source     string
	BufferSize int64
	Targets    []resourced_config.LogTargetConfig

	Data *libmap.TSafeMapStrings
}

// PubSubKey is used for forwarding live log line from TCP listener to every target's channel.
func (b *Base) PubSubKey(targetEndpoint string) string {
	return b.GetSource() + "->" + targetEndpoint
}

// RunBlockingChannel pulls log line from channel continuously.
func (b *Base) RunBlockingChannel(name string, ch <-chan interface{}) {
	for line := range ch {
		b.Data.Append(name, line.(string))
	}
}

// RunBlockingFile tails the file continuously.
func (b *Base) RunBlockingFile(file string) {
	t, err := tail.TailFile(file, tail.Config{
		Follow:   true,
		Location: &tail.SeekInfo{Offset: 0, Whence: os.SEEK_END},
		Logger:   logrus.New(),
	})
	if err == nil {
		if !b.Data.Exists(file) {
			b.Data.Set(file, make([]string, 0))
		}

		for line := range t.Lines {
			b.Data.Append(file, line.Text)
		}
	}
}

// SetSource
func (b *Base) SetSource(source string) {
	b.Source = source
}

// GetSource returns the source field.
func (b *Base) GetSource() string {
	return b.Source
}

// SetBufferSize sets BufferSize
func (b *Base) SetBufferSize(bufferSize int64) {
	b.BufferSize = bufferSize
}

// GetBufferSize returns BufferSize
func (b *Base) GetBufferSize() int64 {
	return b.BufferSize
}

// SetTargets sets []LogTargetConfig
func (b *Base) SetTargets(targets []resourced_config.LogTargetConfig) {
	b.Targets = targets
}

// SetLoglines sets loglines.
func (b *Base) SetLoglines(source string, loglines []string) {
	b.Data.Set(source, loglines)
}

// GetLoglines returns loglines.
func (b *Base) GetLoglines(source string) []string {
	return b.Data.Get(source)
}

// GetLoglinesLength returns the count of loglines.
func (b *Base) GetLoglinesLength(source string) int {
	return len(b.Data.Get(source))
}

// GetAndResetLoglines returns loglines.
func (b *Base) GetAndResetLoglines(source string) []string {
	loglines := b.Data.Get(source)
	b.ResetLoglines(source)
	return loglines
}

// ResetLoglines wipes it clean.
func (b *Base) ResetLoglines(source string) {
	b.Data.Reset(source)
}

// GetTargets returns slice of LogTargetConfig.
func (b *Base) GetTargets() []resourced_config.LogTargetConfig {
	return b.Targets
}

func (b *Base) LogErrorAndResetLoglinesIfNeeded(source string, err error, message string) {
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"Error": err.Error(),
		}).Error(message)

		// Check if we have to prune in-memory log lines.
		if int64(b.GetLoglinesLength(source)) > b.GetBufferSize() {
			b.ResetLoglines(source)
		}
	}
}

// denyLoglines filters out every log line that matches denyList regex.
func (b *Base) denyLoglines(loglines []string, denyList []string) []string {
	newLoglines := make([]string, 0)

	for _, logline := range loglines {
		for _, deny := range denyList {
			if deny == "" {
				newLoglines = append(newLoglines, logline)
				continue
			}

			match, err := regexp.MatchString(deny, logline)
			if err != nil || !match {
				newLoglines = append(newLoglines, logline)
			}
		}
	}

	return newLoglines
}

// allowLoglines filters out every log line that matches allowList regex.
func (b *Base) allowLoglines(loglines []string, allowList []string) []string {
	newLoglines := make([]string, 0)

	for _, logline := range loglines {
		for _, allow := range allowList {
			match, err := regexp.MatchString(allow, logline)
			if err == nil && match {
				newLoglines = append(newLoglines, logline)
			}
		}
	}

	return newLoglines
}

// ProcessOutgoingLoglines before forwarding to targets.
func (b *Base) ProcessOutgoingLoglines(loglines []string, allowList []string, denyList []string) []string {
	loglines = libstring.StitchIndentedInLoglines(loglines)

	if len(allowList) > 0 {
		return b.allowLoglines(loglines, allowList)
	}
	if len(denyList) > 0 {
		return b.denyLoglines(loglines, denyList)
	}

	return loglines
}

// logPayloadForMaster packages the log data before sending to master.
func (b *Base) logPayloadForMaster(hostData *host.Host, loglines []string, source string) AgentLogPayload {
	toSend := AgentLogPayload{}
	toSend.Host.Name = hostData.Name
	toSend.Host.Tags = hostData.Tags
	toSend.Data.Filename = source
	toSend.Data.Loglines = make([]AgentLoglinePayload, 0)

	for _, lg := range loglines {
		linePayload := AgentLoglinePayload{}

		wirePayload := resourced_wire.ParseSingle(lg)

		if wirePayload.Created > 0 {
			linePayload.Created = wirePayload.Created
		} else {
			linePayload.Created = time.Now().UTC().Unix()
		}

		if wirePayload.Type == "" {
			linePayload.Content = lg
		} else {
			linePayload.Content = wirePayload.Content
		}

		if linePayload.Content != "" {
			toSend.Data.Loglines = append(toSend.Data.Loglines, linePayload)
		}
	}

	return toSend
}

// SendToMaster sends log lines to master.
func (b *Base) SendToMaster(accessToken, masterURLHost, masterURLPath string, hostData *host.Host, loglines []string, source string) error {
	// Don't do anything if there are no log lines to send.
	if len(loglines) == 0 {
		return nil
	}

	if masterURLPath == "" {
		masterURLPath = "/api/logs"
	}

	data := b.logPayloadForMaster(hostData, loglines, source)

	dataJson, err := json.Marshal(data)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"Error": err.Error(),
		}).Error("Failed to marshal JSON payload for sending logs to ResourceD Master")

		return err
	}

	url := masterURLHost + masterURLPath

	req, err := http.NewRequest("POST", url, bytes.NewBuffer(dataJson))
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"Error": err.Error(),
		}).Error("Failed to create request struct for sending logs to ResourceD Master")

		return err
	}

	req.SetBasicAuth(accessToken, "")

	client := pester.New()
	client.MaxRetries = 3
	client.Backoff = pester.ExponentialJitterBackoff
	client.KeepLog = false

	resp, err := client.Do(req)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"Error":      err.Error(),
			"req.URL":    req.URL.String(),
			"req.Method": req.Method,
		}).Error("Failed to send logs data to ResourceD Master")

		return err
	}

	if resp != nil && resp.Body != nil {
		defer resp.Body.Close()
	}

	return err
}

// SendToAgent sends log lines to another agent.
func (b *Base) SendToAgent(anotherAgentAddr string, maxRetries int, loglines []string, source string) error {
	if len(loglines) == 0 {
		return nil
	}

	conn, err := libtcp.NewConnectionWithRetries(anotherAgentAddr, maxRetries)
	if err != nil {
		return err
	}

	if conn != nil {
		defer conn.Close()

		w := bufio.NewWriter(conn)

		for i, lg := range loglines {
			// Check if each logline is NOT encoded in ResourceD wire protocol
			wire := resourced_wire.ParseSingle(lg)
			if wire.Type == "" {
				wire.Created = time.Now().UTC().Unix()
				wire.Content = lg

				loglines[i] = wire.EncodeBase64()
				lg = loglines[i]
			}

			fmt.Fprint(w, lg)
			w.Flush()
		}
	}

	return err
}

// SendToSyslog sends log lines to syslog endpoint.
func (b *Base) SendToSyslog(protocol string, addr string, priority syslog.Priority, tag string, loglines []string, source string) error {
	if len(loglines) == 0 {
		return nil
	}

	logHandler, err := syslog.Dial(protocol, addr, priority, tag)
	if err != nil {
		return err
	}
	defer logHandler.Close()

	for _, lg := range loglines {
		logHandler.Write([]byte(resourced_wire.ParseSingle(lg).PlainContent()))
	}

	return nil
}

// SendToGenericTCP sends log lines to a generic tcp endpoint.
func (b *Base) SendToGenericTCP(addr string, maxRetries int, loglines []string, source string) error {
	if len(loglines) == 0 {
		return nil
	}

	conn, err := libtcp.NewConnectionWithRetries(addr, maxRetries)
	if err != nil {
		return err
	}

	if conn != nil {
		defer conn.Close()

		w := bufio.NewWriter(conn)

		for _, lg := range loglines {
			fmt.Fprint(w, lg)
			w.Flush()
		}
	}

	return err
}

// WriteToFile writes log lines to local file.
func (b *Base) WriteToFile(targetFile string, loglines []string) error {
	// Check if loglines contain ResourceD base64 wire protocol.
	// If so, convert to plain text.
	for i, lg := range loglines {
		wire := resourced_wire.ParseSingle(lg)
		if wire.Type != "" {
			loglines[i] = wire.PlainContent()
		}
	}

	fileHandle, err := os.OpenFile(targetFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		return err
	}
	defer fileHandle.Close()

	for _, logline := range loglines {
		if strings.HasSuffix(logline, "\n") {
			fileHandle.WriteString(logline)
		} else {
			fileHandle.WriteString(logline + "\n")
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/codegangsta/gin"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

var startTime = time.Now()

var helpTemplate = "usage: {{.Name}} [-v|--version] [-h|--help] [--port=<port>] <url>\n"

func main() {
	// override the app help template
	cli.AppHelpTemplate = helpTemplate

	app := cli.NewApp()
	app.Name = "gin"
	app.Usage = "A development for go web apps"
	app.Action = MainAction
	app.Flags = []cli.Flag{
		cli.IntFlag{"port", 5678, "port for the proxy server"},
	}

	app.Run(os.Args)
}

func MainAction(c *cli.Context) {

  logger := log.New(os.Stdout, "[gin] ", 0)

	port := c.Int("port")
	appPort := strconv.Itoa(port + 1)

	os.Setenv("PORT", appPort)

	wd, err := os.Getwd()
	if err != nil {
    logger.Fatal(err)
	}

	builder := gin.NewBuilder(".")
	runner := gin.NewRunner(filepath.Join(wd, filepath.Base(wd)))
	runner.SetWriter(os.Stdout)
	proxy := gin.NewProxy(builder, runner)

	config := &gin.Config{
		Port:    port,
		ProxyTo: "http://localhost:" + appPort,
	}

	err = proxy.Run(config)
	if err != nil {
    logger.Fatal(err)
	}

  logger.Printf("listening on port %d\n", port)

	// build right now
	build(builder, logger)

	// scan for changes
	scanChanges(func(path string) {
		build(builder, logger)
	})
}

func build(builder gin.Builder, logger *log.Logger) {
	err := builder.Build()
	if err != nil {
    logger.Println("ERROR! Compilation failed.")
    fmt.Println(builder.Errors())
	}
	time.Sleep(100 * time.Millisecond)
}

type scanCallback func(path string)

func scanChanges(cb scanCallback) {
	for {
		filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
			// TODO load ignore from config
			if path == ".git" {
				return filepath.SkipDir
			}

			if filepath.Ext(path) == ".go" && info.ModTime().After(startTime) {
				cb(path)
				startTime = time.Now()
				return errors.New("done")
			}

			return nil
		})
		time.Sleep(500 * time.Millisecond)
	}
}
<commit_msg>Default to port 3000. more port flags<commit_after>package main

import (
	"errors"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/codegangsta/gin"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

var startTime = time.Now()

var helpTemplate = "usage: {{.Name}} [-v|--version] [-h|--help] [(-p|--port)=<port>] <url>\n"

func main() {
	// override the app help template
	cli.AppHelpTemplate = helpTemplate

	app := cli.NewApp()
	app.Name = "gin"
	app.Usage = "A development server for martini"
	app.Action = MainAction
	app.Flags = []cli.Flag{
		cli.IntFlag{"port,p", 3000, "port for the proxy server"},
	}

	app.Run(os.Args)
}

func MainAction(c *cli.Context) {

	logger := log.New(os.Stdout, "[gin] ", 0)

	port := c.Int("port")
	appPort := strconv.Itoa(port + 1)

	os.Setenv("PORT", appPort)

	wd, err := os.Getwd()
	if err != nil {
		logger.Fatal(err)
	}

	builder := gin.NewBuilder(".")
	runner := gin.NewRunner(filepath.Join(wd, filepath.Base(wd)))
	runner.SetWriter(os.Stdout)
	proxy := gin.NewProxy(builder, runner)

	config := &gin.Config{
		Port:    port,
		ProxyTo: "http://localhost:" + appPort,
	}

	err = proxy.Run(config)
	if err != nil {
		logger.Fatal(err)
	}

	logger.Printf("listening on port %d\n", port)

	// build right now
	build(builder, logger)

	// scan for changes
	scanChanges(func(path string) {
		build(builder, logger)
	})
}

func build(builder gin.Builder, logger *log.Logger) {
	err := builder.Build()
	if err != nil {
		logger.Println("ERROR! Compilation failed.")
		fmt.Println(builder.Errors())
	}
	time.Sleep(100 * time.Millisecond)
}

type scanCallback func(path string)

func scanChanges(cb scanCallback) {
	for {
		filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
			// TODO load ignore from config
			if path == ".git" {
				return filepath.SkipDir
			}

			if filepath.Ext(path) == ".go" && info.ModTime().After(startTime) {
				cb(path)
				startTime = time.Now()
				return errors.New("done")
			}

			return nil
		})
		time.Sleep(500 * time.Millisecond)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"github.com/fatih/color"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"bytes"
)

func sh(shell string) error {
	if verbose {
		color.Yellow(fmt.Sprintf("+ %s\n", shell))
	}
	cmd := exec.Command("/bin/sh", "-c", shell)

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	trap := make(chan os.Signal, 1)
	signal.Notify(trap, syscall.SIGINT)
	defer close(trap)
	defer signal.Stop(trap)

	err := cmd.Start()
	if err != nil {
		return err
	}

	go func() {
		_, ok := <-trap
		if ok {
			fmt.Println("^C")
			cmd.Process.Kill()
		}
	}()

	return cmd.Wait()
}

func shHandler(shell string, outputHandler func(string)) error {
	if verbose {
		color.Yellow(fmt.Sprintf("+ %s\n", shell))
	}
	cmd := exec.Command("/bin/sh", "-c", shell)

	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	reader := bufio.NewReader(stdout)

	trap := make(chan os.Signal, 1)
	signal.Notify(trap, syscall.SIGINT)
	defer close(trap)
	defer signal.Stop(trap)

	cmd.Start()
	go func() {
		_, ok := <-trap
		if ok {
			fmt.Println("^C")
			cmd.Process.Kill()
		}
	}()

	for {
		line, _, err := reader.ReadLine()
		if err != nil {
			if err != io.EOF {
				return err
			}
			return cmd.Wait()
		}
		outputHandler(string(line))
	}
}

func kubectl(cmd string) string {
	buffer := bytes.NewBufferString("kubectl")
	if context != "" {
		buffer.WriteString(" --context=")
		buffer.WriteString(context)
	}
	if namespace != "" {
		buffer.WriteString(" --namespace=")
		buffer.WriteString(namespace)
	}
	buffer.WriteString(" ")
	buffer.WriteString(cmd)
	return buffer.String()
}
<commit_msg>improve context and namespace options insertion<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"github.com/fatih/color"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"regexp"
	"syscall"
)

var (
	cmdPattern   *regexp.Regexp
)

func sh(shell string) error {
	if verbose {
		color.Yellow(fmt.Sprintf("+ %s\n", shell))
	}
	cmd := exec.Command("/bin/sh", "-c", shell)

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	trap := make(chan os.Signal, 1)
	signal.Notify(trap, syscall.SIGINT)
	defer close(trap)
	defer signal.Stop(trap)

	err := cmd.Start()
	if err != nil {
		return err
	}

	go func() {
		_, ok := <-trap
		if ok {
			fmt.Println("^C")
			cmd.Process.Kill()
		}
	}()

	return cmd.Wait()
}

func shHandler(shell string, outputHandler func(string)) error {
	if verbose {
		color.Yellow(fmt.Sprintf("+ %s\n", shell))
	}
	cmd := exec.Command("/bin/sh", "-c", shell)

	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	reader := bufio.NewReader(stdout)

	trap := make(chan os.Signal, 1)
	signal.Notify(trap, syscall.SIGINT)
	defer close(trap)
	defer signal.Stop(trap)

	cmd.Start()
	go func() {
		_, ok := <-trap
		if ok {
			fmt.Println("^C")
			cmd.Process.Kill()
		}
	}()

	for {
		line, _, err := reader.ReadLine()
		if err != nil {
			if err != io.EOF {
				return err
			}
			return cmd.Wait()
		}
		outputHandler(string(line))
	}
}

func kubectl(cmd string) string {
	// Kubectl plugins are only invoked only if all arguments are specified
	// after the command name itself.
	//   kubectl foo --namespace=foo # works
	//   kubectl --namespace=foo foo # fails
	// https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/
	//
	// The `cmd` given can contain a very complex bash input. Instead of parsing it as a whole,
	// we will find the first control character and place the arguments just before it.
	//
	// This fails on some edge case inputs, such as `foo bar=z`, but currently this syntax always
	// takes an option such as `get pods -l foo=bar`, so the `-l` would be parsed first.

	if cmdPattern == nil {
		cmdPattern = regexp.MustCompile(`[^\w\s]`)
	}

	splitAt := cmdPattern.FindStringIndex(cmd)
	if splitAt == nil {
		splitAt = []int{len(cmd), len(cmd)}
	}
	cmdA := cmd[:splitAt[0]]
	cmdB := cmd[splitAt[0]:]

	buffer := bytes.NewBufferString("kubectl ")
	buffer.WriteString(cmdA)
	if context != "" {
		buffer.WriteString(" --context=")
		buffer.WriteString(context)
	}
	if namespace != "" {
		buffer.WriteString(" --namespace=")
		buffer.WriteString(namespace)
	}
	buffer.WriteString(" ")
	buffer.WriteString(cmdB)
	return buffer.String()
}
<|endoftext|>


<commit_before>package exec

import (
	"errors"
	"fmt"
	"github.com/viant/endly"
	"github.com/viant/endly/model"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/ssh"
	"github.com/viant/toolbox/url"
	"path"
)

var sessionsKey = (*model.Sessions)(nil)

//TerminalSessions returns system sessions
func TerminalSessions(context *endly.Context) model.Sessions {
	var result *model.Sessions

	if !context.Contains(sessionsKey) {
		var sessions model.Sessions = make(map[string]*model.Session)
		result = &sessions
		context.AsyncUnsafeKeys[sessionsKey] = true
		_ = context.Put(sessionsKey, result)
	} else {
		context.GetInto(sessionsKey, &result)
	}
	return *result
}

//SessionID returns session I
func SessionID(context *endly.Context, target *url.Resource) string {
	username := ""
	if config, _ := context.Secrets.GetCredentials(target.Credentials); config != nil {
		username = config.Username
	}
	return  username + "@" + target.Host()
}

//TerminalSession returns Session for passed in target resource.
func TerminalSession(context *endly.Context, target *url.Resource) (*model.Session, error) {
	sessions := TerminalSessions(context)
	if target == nil {
		return nil, errors.New("target was empty")
	}
	var sessionID = SessionID(context, target)

	if !sessions.Has(sessionID) {
		service, err := context.Service(ServiceID)
		if err != nil {
			return nil, err
		}
		response := service.Run(context, &OpenSessionRequest{
			Target: target,
		})
		if response.Err != nil {
			return nil, response.Err
		}
	}
	return sessions[sessionID], nil
}

//Os returns operating system for provide session
func OperatingSystem(context *endly.Context, sessionName string) *model.OperatingSystem {
	var sessions = TerminalSessions(context)
	if session, has := sessions[sessionName]; has {
		return session.Os
	}
	return nil
}

func openSSHSession(context *endly.Context, target *url.Resource, commandDirectory string, service ssh.Service) error {
	request := &OpenSessionRequest{
		Target:        target,
		Basedir:       commandDirectory,
		ReplayService: service,
	}
	response := &OpenSessionResponse{}
	if err := endly.Run(context, request, response); err != nil {
		return err
	}
	if _, ok := TerminalSessions(context)[":"]; !ok {
		TerminalSessions(context)[":"] = TerminalSessions(context)[response.SessionID]
	}
	return nil
}

//NewSSHRecodingContext open recorder context (to capture SSH command)
func NewSSHRecodingContext(manager endly.Manager, target *url.Resource, sessionDir string) (*endly.Context, error) {
	return NewSSHMultiRecordingContext(manager, map[string]*url.Resource{
		sessionDir: target,
	})
}

//NewSSHMultiRecordingContext open multi recorded session
func NewSSHMultiRecordingContext(manager endly.Manager, sessions map[string]*url.Resource) (*endly.Context, error) {
	context := manager.NewContext(toolbox.NewContext())
	fileName, _, _ := toolbox.CallerInfo(4)
	parent, _ := path.Split(fileName)
	for baseDir, target := range sessions {
		baseDir = path.Join(parent, baseDir)
		if err := openSSHSession(context, target, baseDir, nil); err != nil {
			return nil, err
		}
	}
	return context, nil

}

//GetReplayService return replay service
func GetReplayService(basedir string) (ssh.Service, error) {
	fileName, _, _ := toolbox.DiscoverCaller(3, 10, "helper.go")
	parent, _ := path.Split(fileName)
	replayDirectory := path.Join(parent, basedir)
	if !toolbox.FileExists(replayDirectory) {
		return nil, fmt.Errorf("replay directory does not exist: %v", replayDirectory)
	}
	commands, err := ssh.NewReplayCommands(path.Join(parent, basedir))
	if err != nil {
		return nil, err
	}
	err = commands.Load()
	if err != nil {
		return nil, err
	}
	service := ssh.NewReplayService(commands.Shell(), commands.System(), commands, nil)
	return service, nil
}

//NewSSHReplayContext opens test context with SSH commands to replay
func NewSSHReplayContext(manager endly.Manager, target *url.Resource, basedir string) (*endly.Context, error) {
	return NewSSHMultiReplayContext(manager, map[string]*url.Resource{
		basedir: target,
	})
}

//OpenMultiSessionTestContext opens test context with multi SSH replay/mocks session
func NewSSHMultiReplayContext(manager endly.Manager, sessions map[string]*url.Resource) (*endly.Context, error) {
	context := manager.NewContext(nil)
	for baseDir, target := range sessions {
		service, err := GetReplayService(baseDir)
		if err != nil {
			return nil, err
		}
		if err := openSSHSession(context, target, "", service); err != nil {
			return nil, err
		}
	}
	return context, nil
}
<commit_msg>reformatted<commit_after>package exec

import (
	"errors"
	"fmt"
	"github.com/viant/endly"
	"github.com/viant/endly/model"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/ssh"
	"github.com/viant/toolbox/url"
	"path"
)

var sessionsKey = (*model.Sessions)(nil)

//TerminalSessions returns system sessions
func TerminalSessions(context *endly.Context) model.Sessions {
	var result *model.Sessions

	if !context.Contains(sessionsKey) {
		var sessions model.Sessions = make(map[string]*model.Session)
		result = &sessions
		context.AsyncUnsafeKeys[sessionsKey] = true
		_ = context.Put(sessionsKey, result)
	} else {
		context.GetInto(sessionsKey, &result)
	}
	return *result
}

//SessionID returns session I
func SessionID(context *endly.Context, target *url.Resource) string {
	username := ""
	if config, _ := context.Secrets.GetCredentials(target.Credentials); config != nil {
		username = config.Username
	}
	return username + "@" + target.Host()
}

//TerminalSession returns Session for passed in target resource.
func TerminalSession(context *endly.Context, target *url.Resource) (*model.Session, error) {
	sessions := TerminalSessions(context)
	if target == nil {
		return nil, errors.New("target was empty")
	}
	var sessionID = SessionID(context, target)

	if !sessions.Has(sessionID) {
		service, err := context.Service(ServiceID)
		if err != nil {
			return nil, err
		}
		response := service.Run(context, &OpenSessionRequest{
			Target: target,
		})
		if response.Err != nil {
			return nil, response.Err
		}
	}
	return sessions[sessionID], nil
}

//Os returns operating system for provide session
func OperatingSystem(context *endly.Context, sessionName string) *model.OperatingSystem {
	var sessions = TerminalSessions(context)
	if session, has := sessions[sessionName]; has {
		return session.Os
	}
	return nil
}

func openSSHSession(context *endly.Context, target *url.Resource, commandDirectory string, service ssh.Service) error {
	request := &OpenSessionRequest{
		Target:        target,
		Basedir:       commandDirectory,
		ReplayService: service,
	}
	response := &OpenSessionResponse{}
	if err := endly.Run(context, request, response); err != nil {
		return err
	}
	if _, ok := TerminalSessions(context)[":"]; !ok {
		TerminalSessions(context)[":"] = TerminalSessions(context)[response.SessionID]
	}
	return nil
}

//NewSSHRecodingContext open recorder context (to capture SSH command)
func NewSSHRecodingContext(manager endly.Manager, target *url.Resource, sessionDir string) (*endly.Context, error) {
	return NewSSHMultiRecordingContext(manager, map[string]*url.Resource{
		sessionDir: target,
	})
}

//NewSSHMultiRecordingContext open multi recorded session
func NewSSHMultiRecordingContext(manager endly.Manager, sessions map[string]*url.Resource) (*endly.Context, error) {
	context := manager.NewContext(toolbox.NewContext())
	fileName, _, _ := toolbox.CallerInfo(4)
	parent, _ := path.Split(fileName)
	for baseDir, target := range sessions {
		baseDir = path.Join(parent, baseDir)
		if err := openSSHSession(context, target, baseDir, nil); err != nil {
			return nil, err
		}
	}
	return context, nil

}

//GetReplayService return replay service
func GetReplayService(basedir string) (ssh.Service, error) {
	fileName, _, _ := toolbox.DiscoverCaller(3, 10, "helper.go")
	parent, _ := path.Split(fileName)
	replayDirectory := path.Join(parent, basedir)
	if !toolbox.FileExists(replayDirectory) {
		return nil, fmt.Errorf("replay directory does not exist: %v", replayDirectory)
	}
	commands, err := ssh.NewReplayCommands(path.Join(parent, basedir))
	if err != nil {
		return nil, err
	}
	err = commands.Load()
	if err != nil {
		return nil, err
	}
	service := ssh.NewReplayService(commands.Shell(), commands.System(), commands, nil)
	return service, nil
}

//NewSSHReplayContext opens test context with SSH commands to replay
func NewSSHReplayContext(manager endly.Manager, target *url.Resource, basedir string) (*endly.Context, error) {
	return NewSSHMultiReplayContext(manager, map[string]*url.Resource{
		basedir: target,
	})
}

//OpenMultiSessionTestContext opens test context with multi SSH replay/mocks session
func NewSSHMultiReplayContext(manager endly.Manager, sessions map[string]*url.Resource) (*endly.Context, error) {
	context := manager.NewContext(nil)
	for baseDir, target := range sessions {
		service, err := GetReplayService(baseDir)
		if err != nil {
			return nil, err
		}
		if err := openSSHSession(context, target, "", service); err != nil {
			return nil, err
		}
	}
	return context, nil
}
<|endoftext|>


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

package main

import (
	"fmt"
)

// TODO: need way to track dead cows (non-beef)
// TODO: need a way to pass runtime config parameters to the different
// feed types, such as to configure cbdatasource options for DCP feed.
// TODO: need a way to collect these errors so REST api
// can show them to user ("hey, perhaps you deleted a bucket
// and should delete these related full-text indexes?
// or the couchbase cluster is just down.");
// perhaps as specialized clog writer?

func (mgr *Manager) startFeedByType(feedName, indexName, indexUUID,
	sourceType, sourceName, sourceUUID string,
	dests map[string]Dest) error {
	if sourceType == "couchbase" ||
		sourceType == "couchbase-dcp" {
		return mgr.startDCPFeed(feedName, indexName, indexUUID,
			sourceName, sourceUUID, dests)
	}

	if sourceType == "couchbase-tap" {
		return mgr.startTAPFeed(feedName, indexName, indexUUID,
			sourceName, sourceUUID, dests)
	}
	if sourceType == "dest" {
		return mgr.startDestFeed(feedName, dests)
	}

	if sourceType == "nil" {
		return mgr.registerFeed(NewNILFeed(feedName, dests))
	}

	return fmt.Errorf("error: startFeed() got unknown source type: %s", sourceType)
}

func (mgr *Manager) startDCPFeed(feedName, indexName, indexUUID,
	bucketName, bucketUUID string, dests map[string]Dest) error {
	feed, err := NewDCPFeed(feedName, mgr.server, "default",
		bucketName, bucketUUID, BasicPartitionFunc, dests)
	if err != nil {
		return fmt.Errorf("error: could not prepare DCP stream to server: %s,"+
			" bucketName: %s, indexName: %s, err: %v",
			mgr.server, bucketName, indexName, err)
	}
	if err = feed.Start(); err != nil {
		return fmt.Errorf("error: could not start dcp feed, server: %s, err: %v",
			mgr.server, err)
	}
	if err = mgr.registerFeed(feed); err != nil {
		feed.Close()
		return err
	}
	return nil
}

func (mgr *Manager) startTAPFeed(feedName, indexName, indexUUID,
	bucketName, bucketUUID string, dests map[string]Dest) error {
	feed, err := NewTAPFeed(feedName, mgr.server, "default",
		bucketName, bucketUUID, BasicPartitionFunc, dests)
	if err != nil {
		return fmt.Errorf("error: could not prepare TAP stream to server: %s,"+
			" bucketName: %s, indexName: %s, err: %v",
			mgr.server, bucketName, indexName, err)
	}
	if err = feed.Start(); err != nil {
		return fmt.Errorf("error: could not start tap feed, server: %s, err: %v",
			mgr.server, err)
	}
	if err = mgr.registerFeed(feed); err != nil {
		feed.Close()
		return err
	}
	return nil
}

func (mgr *Manager) startDestFeed(feedName string,
	dests map[string]Dest) error {
	feed, err := NewDestFeed(feedName, BasicPartitionFunc, dests)
	if err != nil {
		return err
	}
	if err = feed.Start(); err != nil {
		return fmt.Errorf("error: could not start dest feed, server: %s, err: %v",
			mgr.server, err)
	}
	if err = mgr.registerFeed(feed); err != nil {
		feed.Close()
		return err
	}

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

package main

import (
	"fmt"
)

// TODO: need way to track dead cows (non-beef)
// TODO: need a way to pass runtime config parameters to the different
// feed types, such as to configure cbdatasource options for DCP feed.
// TODO: need a way to collect these errors so REST api
// can show them to user ("hey, perhaps you deleted a bucket
// and should delete these related full-text indexes?
// or the couchbase cluster is just down.");
// perhaps as specialized clog writer?

func (mgr *Manager) startFeedByType(feedName, indexName, indexUUID,
	sourceType, sourceName, sourceUUID string,
	dests map[string]Dest) error {
	if sourceType == "couchbase" ||
		sourceType == "couchbase-dcp" {
		return mgr.startDCPFeed(feedName, indexName, indexUUID,
			sourceName, sourceUUID, dests)
	}

	if sourceType == "couchbase-tap" {
		return mgr.startTAPFeed(feedName, indexName, indexUUID,
			sourceName, sourceUUID, dests)
	}

	if sourceType == "dest" {
		return mgr.startDestFeed(feedName, dests)
	}

	if sourceType == "nil" {
		return mgr.registerFeed(NewNILFeed(feedName, dests))
	}

	return fmt.Errorf("error: startFeed() got unknown source type: %s", sourceType)
}

func (mgr *Manager) startDCPFeed(feedName, indexName, indexUUID,
	bucketName, bucketUUID string, dests map[string]Dest) error {
	feed, err := NewDCPFeed(feedName, mgr.server, "default",
		bucketName, bucketUUID, BasicPartitionFunc, dests)
	if err != nil {
		return fmt.Errorf("error: could not prepare DCP stream to server: %s,"+
			" bucketName: %s, indexName: %s, err: %v",
			mgr.server, bucketName, indexName, err)
	}
	if err = feed.Start(); err != nil {
		return fmt.Errorf("error: could not start dcp feed, server: %s, err: %v",
			mgr.server, err)
	}
	if err = mgr.registerFeed(feed); err != nil {
		feed.Close()
		return err
	}
	return nil
}

func (mgr *Manager) startTAPFeed(feedName, indexName, indexUUID,
	bucketName, bucketUUID string, dests map[string]Dest) error {
	feed, err := NewTAPFeed(feedName, mgr.server, "default",
		bucketName, bucketUUID, BasicPartitionFunc, dests)
	if err != nil {
		return fmt.Errorf("error: could not prepare TAP stream to server: %s,"+
			" bucketName: %s, indexName: %s, err: %v",
			mgr.server, bucketName, indexName, err)
	}
	if err = feed.Start(); err != nil {
		return fmt.Errorf("error: could not start tap feed, server: %s, err: %v",
			mgr.server, err)
	}
	if err = mgr.registerFeed(feed); err != nil {
		feed.Close()
		return err
	}
	return nil
}

func (mgr *Manager) startDestFeed(feedName string,
	dests map[string]Dest) error {
	feed, err := NewDestFeed(feedName, BasicPartitionFunc, dests)
	if err != nil {
		return err
	}
	if err = feed.Start(); err != nil {
		return fmt.Errorf("error: could not start dest feed, server: %s, err: %v",
			mgr.server, err)
	}
	if err = mgr.registerFeed(feed); err != nil {
		feed.Close()
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// The version package implements version parsing.
// It also acts as guardian of the current client Juju version number.
package version

import (
	"fmt"
	"io/ioutil"
	"labix.org/v2/mgo/bson"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

// The presence and format of this constant is very important.
// The debian/rules build recipe uses this value for the version
// number of the release package.
const version = "1.9.13"

// CurrentNumber returns the version number.
func CurrentNumber() Number {
	return MustParse(version)
}

// CurrentSeries returns the current Ubuntu release name.
func CurrentSeries() string {
	return readSeries("/etc/lsb-release")
}

// CurrentArch returns the architecture of the machine.
func CurrentArch() string {
	return ubuntuArch(runtime.GOARCH)
}

// Current gives the current version of the system.  If the file
// "FORCE-VERSION" is present in the same directory as the running
// binary, it will override this.
var Current = Binary{
	Number: CurrentNumber(),
	Series: CurrentSeries(),
	Arch:   CurrentArch(),
}

func init() {
	toolsDir := filepath.Dir(os.Args[0])
	v, err := ioutil.ReadFile(filepath.Join(toolsDir, "FORCE-VERSION"))
	if err != nil {
		if os.IsNotExist(err) {
			return
		}
		panic(fmt.Errorf("version: cannot read forced version: %v", err))
	}
	Current.Number = MustParse(strings.TrimSpace(string(v)))
}

// Number represents a juju version.  When bugs are fixed the patch
// number is incremented; when new features are added the minor number
// is incremented and patch is reset; and when compatibility is broken
// the major version is incremented and minor and patch are reset.  The
// build number is automatically assigned and has no well defined
// sequence.  If the build number is greater than zero or any of the
// other numbers are odd, it indicates that the release is still in
// development.
type Number struct {
	Major int
	Minor int
	Patch int
	Build int
}

// Binary specifies a binary version of juju.
type Binary struct {
	Number
	Series string
	Arch   string
}

func (v Binary) String() string {
	return fmt.Sprintf("%v-%s-%s", v.Number, v.Series, v.Arch)
}

// GetBSON turns v into a bson.Getter so it can be saved directly
// on a MongoDB database with mgo.
func (v Binary) GetBSON() (interface{}, error) {
	return v.String(), nil
}

// SetBSON turns v into a bson.Setter so it can be loaded directly
// from a MongoDB database with mgo.
func (vp *Binary) SetBSON(raw bson.Raw) error {
	var s string
	err := raw.Unmarshal(&s)
	if err != nil {
		return err
	}
	v, err := ParseBinary(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

var (
	binaryPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(\.\d{1,9})?-([^-]+)-([^-]+)$`)
	numberPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(\.\d{1,9})?$`)
)

// MustParse parses a version and panics if it does
// not parse correctly.
func MustParse(s string) Number {
	v, err := Parse(s)
	if err != nil {
		panic(err)
	}
	return v
}

// MustParseBinary parses a binary version and panics if it does
// not parse correctly.
func MustParseBinary(s string) Binary {
	v, err := ParseBinary(s)
	if err != nil {
		panic(err)
	}
	return v
}

// ParseBinary parses a binary version of the form "1.2.3-series-arch".
func ParseBinary(s string) (Binary, error) {
	m := binaryPat.FindStringSubmatch(s)
	if m == nil {
		return Binary{}, fmt.Errorf("invalid binary version %q", s)
	}
	var v Binary
	v.Major = atoi(m[1])
	v.Minor = atoi(m[2])
	v.Patch = atoi(m[3])
	if m[4] != "" {
		v.Build = atoi(m[4][1:])
	}
	v.Series = m[5]
	v.Arch = m[6]
	return v, nil
}

// Parse parses the version, which is of the form 1.2.3
// giving the major, minor and release versions
// respectively.
func Parse(s string) (Number, error) {
	m := numberPat.FindStringSubmatch(s)
	if m == nil {
		return Number{}, fmt.Errorf("invalid version %q", s)
	}
	var v Number
	v.Major = atoi(m[1])
	v.Minor = atoi(m[2])
	v.Patch = atoi(m[3])
	if m[4] != "" {
		v.Build = atoi(m[4][1:])
	}
	return v, nil
}

// atoi is the same as strconv.Atoi but assumes that
// the string has been verified to be a valid integer.
func atoi(s string) int {
	n, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return n
}

func (v Number) String() string {
	s := fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
	if v.Build > 0 {
		s += fmt.Sprintf(".%d", v.Build)
	}
	return s
}

// Less returns whether v is semantically earlier in the
// version sequence than w.
func (v Number) Less(w Number) bool {
	switch {
	case v.Major != w.Major:
		return v.Major < w.Major
	case v.Minor != w.Minor:
		return v.Minor < w.Minor
	case v.Patch != w.Patch:
		return v.Patch < w.Patch
	case v.Build != w.Build:
		return v.Build < w.Build
	}
	return false
}

// GetBSON turns v into a bson.Getter so it can be saved directly
// on a MongoDB database with mgo.
func (v Number) GetBSON() (interface{}, error) {
	return v.String(), nil
}

// SetBSON turns v into a bson.Setter so it can be loaded directly
// from a MongoDB database with mgo.
func (vp *Number) SetBSON(raw bson.Raw) error {
	var s string
	err := raw.Unmarshal(&s)
	if err != nil {
		return err
	}
	v, err := Parse(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

func isOdd(x int) bool {
	return x%2 != 0
}

// IsDev returns whether the version represents a development
// version. A version with an odd-numbered major, minor
// or patch version is considered to be a development version.
func (v Number) IsDev() bool {
	return isOdd(v.Major) || isOdd(v.Minor) || isOdd(v.Patch) || v.Build > 0
}

func readSeries(releaseFile string) string {
	data, err := ioutil.ReadFile(releaseFile)
	if err != nil {
		return "unknown"
	}
	for _, line := range strings.Split(string(data), "\n") {
		const p = "DISTRIB_CODENAME="
		if strings.HasPrefix(line, p) {
			return strings.Trim(line[len(p):], "\t '\"")
		}
	}
	return "unknown"
}

func ubuntuArch(arch string) string {
	if arch == "386" {
		arch = "i386"
	}
	return arch
}
<commit_msg>version: set development version to 1.9.14<commit_after>// The version package implements version parsing.
// It also acts as guardian of the current client Juju version number.
package version

import (
	"fmt"
	"io/ioutil"
	"labix.org/v2/mgo/bson"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

// The presence and format of this constant is very important.
// The debian/rules build recipe uses this value for the version
// number of the release package.
const version = "1.9.14"

// CurrentNumber returns the version number.
func CurrentNumber() Number {
	return MustParse(version)
}

// CurrentSeries returns the current Ubuntu release name.
func CurrentSeries() string {
	return readSeries("/etc/lsb-release")
}

// CurrentArch returns the architecture of the machine.
func CurrentArch() string {
	return ubuntuArch(runtime.GOARCH)
}

// Current gives the current version of the system.  If the file
// "FORCE-VERSION" is present in the same directory as the running
// binary, it will override this.
var Current = Binary{
	Number: CurrentNumber(),
	Series: CurrentSeries(),
	Arch:   CurrentArch(),
}

func init() {
	toolsDir := filepath.Dir(os.Args[0])
	v, err := ioutil.ReadFile(filepath.Join(toolsDir, "FORCE-VERSION"))
	if err != nil {
		if os.IsNotExist(err) {
			return
		}
		panic(fmt.Errorf("version: cannot read forced version: %v", err))
	}
	Current.Number = MustParse(strings.TrimSpace(string(v)))
}

// Number represents a juju version.  When bugs are fixed the patch
// number is incremented; when new features are added the minor number
// is incremented and patch is reset; and when compatibility is broken
// the major version is incremented and minor and patch are reset.  The
// build number is automatically assigned and has no well defined
// sequence.  If the build number is greater than zero or any of the
// other numbers are odd, it indicates that the release is still in
// development.
type Number struct {
	Major int
	Minor int
	Patch int
	Build int
}

// Binary specifies a binary version of juju.
type Binary struct {
	Number
	Series string
	Arch   string
}

func (v Binary) String() string {
	return fmt.Sprintf("%v-%s-%s", v.Number, v.Series, v.Arch)
}

// GetBSON turns v into a bson.Getter so it can be saved directly
// on a MongoDB database with mgo.
func (v Binary) GetBSON() (interface{}, error) {
	return v.String(), nil
}

// SetBSON turns v into a bson.Setter so it can be loaded directly
// from a MongoDB database with mgo.
func (vp *Binary) SetBSON(raw bson.Raw) error {
	var s string
	err := raw.Unmarshal(&s)
	if err != nil {
		return err
	}
	v, err := ParseBinary(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

var (
	binaryPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(\.\d{1,9})?-([^-]+)-([^-]+)$`)
	numberPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(\.\d{1,9})?$`)
)

// MustParse parses a version and panics if it does
// not parse correctly.
func MustParse(s string) Number {
	v, err := Parse(s)
	if err != nil {
		panic(err)
	}
	return v
}

// MustParseBinary parses a binary version and panics if it does
// not parse correctly.
func MustParseBinary(s string) Binary {
	v, err := ParseBinary(s)
	if err != nil {
		panic(err)
	}
	return v
}

// ParseBinary parses a binary version of the form "1.2.3-series-arch".
func ParseBinary(s string) (Binary, error) {
	m := binaryPat.FindStringSubmatch(s)
	if m == nil {
		return Binary{}, fmt.Errorf("invalid binary version %q", s)
	}
	var v Binary
	v.Major = atoi(m[1])
	v.Minor = atoi(m[2])
	v.Patch = atoi(m[3])
	if m[4] != "" {
		v.Build = atoi(m[4][1:])
	}
	v.Series = m[5]
	v.Arch = m[6]
	return v, nil
}

// Parse parses the version, which is of the form 1.2.3
// giving the major, minor and release versions
// respectively.
func Parse(s string) (Number, error) {
	m := numberPat.FindStringSubmatch(s)
	if m == nil {
		return Number{}, fmt.Errorf("invalid version %q", s)
	}
	var v Number
	v.Major = atoi(m[1])
	v.Minor = atoi(m[2])
	v.Patch = atoi(m[3])
	if m[4] != "" {
		v.Build = atoi(m[4][1:])
	}
	return v, nil
}

// atoi is the same as strconv.Atoi but assumes that
// the string has been verified to be a valid integer.
func atoi(s string) int {
	n, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return n
}

func (v Number) String() string {
	s := fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
	if v.Build > 0 {
		s += fmt.Sprintf(".%d", v.Build)
	}
	return s
}

// Less returns whether v is semantically earlier in the
// version sequence than w.
func (v Number) Less(w Number) bool {
	switch {
	case v.Major != w.Major:
		return v.Major < w.Major
	case v.Minor != w.Minor:
		return v.Minor < w.Minor
	case v.Patch != w.Patch:
		return v.Patch < w.Patch
	case v.Build != w.Build:
		return v.Build < w.Build
	}
	return false
}

// GetBSON turns v into a bson.Getter so it can be saved directly
// on a MongoDB database with mgo.
func (v Number) GetBSON() (interface{}, error) {
	return v.String(), nil
}

// SetBSON turns v into a bson.Setter so it can be loaded directly
// from a MongoDB database with mgo.
func (vp *Number) SetBSON(raw bson.Raw) error {
	var s string
	err := raw.Unmarshal(&s)
	if err != nil {
		return err
	}
	v, err := Parse(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

func isOdd(x int) bool {
	return x%2 != 0
}

// IsDev returns whether the version represents a development
// version. A version with an odd-numbered major, minor
// or patch version is considered to be a development version.
func (v Number) IsDev() bool {
	return isOdd(v.Major) || isOdd(v.Minor) || isOdd(v.Patch) || v.Build > 0
}

func readSeries(releaseFile string) string {
	data, err := ioutil.ReadFile(releaseFile)
	if err != nil {
		return "unknown"
	}
	for _, line := range strings.Split(string(data), "\n") {
		const p = "DISTRIB_CODENAME="
		if strings.HasPrefix(line, p) {
			return strings.Trim(line[len(p):], "\t '\"")
		}
	}
	return "unknown"
}

func ubuntuArch(arch string) string {
	if arch == "386" {
		arch = "i386"
	}
	return arch
}
<|endoftext|>


<commit_before>// A single location for the version since many areas of the codebase rely on this and keeping multiple
// constants in sync is a pain in the back-end. Each component may still get its own version in the future...
package version

const Semantic = "0.7.0"
<commit_msg>updated version<commit_after>// A single location for the version since many areas of the codebase rely on this and keeping multiple
// constants in sync is a pain in the back-end. Each component may still get its own version in the future...
package version

const Semantic = "0.8.0"
<|endoftext|>


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

// Package version implements etcd version parsing and contains latest version
// information.
package version

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/coreos/etcd/pkg/fileutil"
	"github.com/coreos/etcd/pkg/types"
)

var (
	// MinClusterVersion is the min cluster version this etcd binary is compatible with.
	MinClusterVersion = "2.3.0"
	Version           = "3.0.0-beta.0"

	// Git SHA Value will be set during build
	GitSHA = "Not provided (use ./build instead of go build)"
)

// DataDirVersion is an enum for versions of etcd logs.
type DataDirVersion string

const (
	DataDirUnknown  DataDirVersion = "Unknown WAL"
	DataDir2_0      DataDirVersion = "2.0.0"
	DataDir2_0Proxy DataDirVersion = "2.0 proxy"
	DataDir2_0_1    DataDirVersion = "2.0.1"
)

type Versions struct {
	Server  string `json:"etcdserver"`
	Cluster string `json:"etcdcluster"`
	// TODO: raft state machine version
}

func DetectDataDir(dirpath string) (DataDirVersion, error) {
	names, err := fileutil.ReadDir(dirpath)
	if err != nil {
		if os.IsNotExist(err) {
			err = nil
		}
		// Error reading the directory
		return DataDirUnknown, err
	}
	nameSet := types.NewUnsafeSet(names...)
	if nameSet.Contains("member") {
		ver, err := DetectDataDir(path.Join(dirpath, "member"))
		if ver == DataDir2_0 {
			return DataDir2_0_1, nil
		}
		return ver, err
	}
	if nameSet.ContainsAll([]string{"snap", "wal"}) {
		// .../wal cannot be empty to exist.
		walnames, err := fileutil.ReadDir(path.Join(dirpath, "wal"))
		if err == nil && len(walnames) > 0 {
			return DataDir2_0, nil
		}
	}
	if nameSet.ContainsAll([]string{"proxy"}) {
		return DataDir2_0Proxy, nil
	}
	return DataDirUnknown, nil
}

// Cluster only keeps the major.minor.
func Cluster(v string) string {
	vs := strings.Split(v, ".")
	if len(vs) <= 2 {
		return v
	}
	return fmt.Sprintf("%s.%s", vs[0], vs[1])
}
<commit_msg>*: bump to v3.0.0-beta.0+git<commit_after>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package version implements etcd version parsing and contains latest version
// information.
package version

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/coreos/etcd/pkg/fileutil"
	"github.com/coreos/etcd/pkg/types"
)

var (
	// MinClusterVersion is the min cluster version this etcd binary is compatible with.
	MinClusterVersion = "2.3.0"
	Version           = "3.0.0-beta.0+git"

	// Git SHA Value will be set during build
	GitSHA = "Not provided (use ./build instead of go build)"
)

// DataDirVersion is an enum for versions of etcd logs.
type DataDirVersion string

const (
	DataDirUnknown  DataDirVersion = "Unknown WAL"
	DataDir2_0      DataDirVersion = "2.0.0"
	DataDir2_0Proxy DataDirVersion = "2.0 proxy"
	DataDir2_0_1    DataDirVersion = "2.0.1"
)

type Versions struct {
	Server  string `json:"etcdserver"`
	Cluster string `json:"etcdcluster"`
	// TODO: raft state machine version
}

func DetectDataDir(dirpath string) (DataDirVersion, error) {
	names, err := fileutil.ReadDir(dirpath)
	if err != nil {
		if os.IsNotExist(err) {
			err = nil
		}
		// Error reading the directory
		return DataDirUnknown, err
	}
	nameSet := types.NewUnsafeSet(names...)
	if nameSet.Contains("member") {
		ver, err := DetectDataDir(path.Join(dirpath, "member"))
		if ver == DataDir2_0 {
			return DataDir2_0_1, nil
		}
		return ver, err
	}
	if nameSet.ContainsAll([]string{"snap", "wal"}) {
		// .../wal cannot be empty to exist.
		walnames, err := fileutil.ReadDir(path.Join(dirpath, "wal"))
		if err == nil && len(walnames) > 0 {
			return DataDir2_0, nil
		}
	}
	if nameSet.ContainsAll([]string{"proxy"}) {
		return DataDir2_0Proxy, nil
	}
	return DataDirUnknown, nil
}

// Cluster only keeps the major.minor.
func Cluster(v string) string {
	vs := strings.Split(v, ".")
	if len(vs) <= 2 {
		return v
	}
	return fmt.Sprintf("%s.%s", vs[0], vs[1])
}
<|endoftext|>


<commit_before>// Copyright 2016 The Linux Foundation
//
// 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 version

import "fmt"

const (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 1
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 0
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 0

	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = "-rc3"
)

// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
<commit_msg>version: back to development<commit_after>// Copyright 2016 The Linux Foundation
//
// 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 version

import "fmt"

const (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 1
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 0
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 0

	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = "-rc3-dev"
)

// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
<|endoftext|>


<commit_before>package version

// Version is the version of the build.
const Version = "1.2.3"
<commit_msg>Move to v1.2.4-dev<commit_after>package version

// Version is the version of the build.
const Version = "1.2.4-dev"
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"runtime"
)

// Version represents the dh-make-golang build version.
type Version struct {
	major      int
	minor      int
	patch      int
	preRelease string
}

var currentVersion = Version{
	major:      0,
	minor:      3,
	patch:      3,
	preRelease: "",
}

func (v Version) String() string {
	return fmt.Sprintf("%d.%d.%d%s", v.major, v.minor, v.patch, v.preRelease)
}

func buildVersionString() string {
	version := "v" + currentVersion.String()
	osArch := runtime.GOOS + "/" + runtime.GOARCH
	return fmt.Sprintf("%s %s %s", program, version, osArch)
}
<commit_msg>Bump version for release of v0.4.0<commit_after>package main

import (
	"fmt"
	"runtime"
)

// Version represents the dh-make-golang build version.
type Version struct {
	major      int
	minor      int
	patch      int
	preRelease string
}

var currentVersion = Version{
	major:      0,
	minor:      4,
	patch:      0,
	preRelease: "",
}

func (v Version) String() string {
	return fmt.Sprintf("%d.%d.%d%s", v.major, v.minor, v.patch, v.preRelease)
}

func buildVersionString() string {
	version := "v" + currentVersion.String()
	osArch := runtime.GOOS + "/" + runtime.GOARCH
	return fmt.Sprintf("%s %s %s", program, version, osArch)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"runtime"
)

// Version represents the dh-make-golang build version.
type Version struct {
	major      int
	minor      int
	patch      int
	preRelease string
}

var currentVersion = Version{
	major:      0,
	minor:      4,
	patch:      0,
	preRelease: "",
}

func (v Version) String() string {
	return fmt.Sprintf("%d.%d.%d%s", v.major, v.minor, v.patch, v.preRelease)
}

func buildVersionString() string {
	version := "v" + currentVersion.String()
	osArch := runtime.GOOS + "/" + runtime.GOARCH
	return fmt.Sprintf("%s %s %s", program, version, osArch)
}
<commit_msg>Bump version number for v0.5.0 release<commit_after>package main

import (
	"fmt"
	"runtime"
)

// Version represents the dh-make-golang build version.
type Version struct {
	major      int
	minor      int
	patch      int
	preRelease string
}

var currentVersion = Version{
	major:      0,
	minor:      5,
	patch:      0,
	preRelease: "",
}

func (v Version) String() string {
	return fmt.Sprintf("%d.%d.%d%s", v.major, v.minor, v.patch, v.preRelease)
}

func buildVersionString() string {
	version := "v" + currentVersion.String()
	osArch := runtime.GOOS + "/" + runtime.GOARCH
	return fmt.Sprintf("%s %s %s", program, version, osArch)
}
<|endoftext|>