<commit_before>/*
 * 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-vmware.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("", "", isoFilename)
	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 := "Home"

	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
}
<commit_msg>fusion: use mkdir -p for home dir mount<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-vmware.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("", "", isoFilename)
	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 := "Home"

	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 -p "+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 borg

// Think of borg events like inotify events.  We're interested in changes to
// them all.  All events are sent to your instance of borg.  The filtering
// is done once they've arrived.  Let's make keys look like directories to
// aid this comparison.
//
// Example spec:
//
//     /<slug_id>/proc/<type>/<upid>/...
//
// Example interactive session:
//
//     $ borgd -i -a :9999
//     >> ls /proc/123_a3c_a12b3c45/beanstalkd/12345/
//     exe
//     env
//     lock
//     >> cat /proc/123_a3c_a12b3c45/beanstalkd/12345/*
//     beanstalkd -l 0.0.0.0 -p 4563
//     PORT=4563
//     123.4.5.678:9999
//     >>
//
// Example code:
//
//     me, err := borg.ListenAndServe(listenAddr)
//     if err != nil {
//         log.Exitf("listen failed: %v", err)
//     }
//
//     // Handle a specific type of key notification.
//     // The : signals a named variable part.
//     me.HandleFunc(
//         "/proc/:slug/beanstalkd/:upid/lock",
//         func (msg *borg.Message) {
//             if msg.Value == myId {
//                 cmd := beanstalkd ....
//                 ... launch beanstalkd ...
//                 me.Echo(cmd, "/proc/<slug>/beanstalkd/<upid>/cmd")
//             }
//         },
//     )

import (
	"borg/paxos"
	"borg/store"
	"borg/util"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
)

const (
	mFrom = iota
	mTo
	mCmd
	mBody
	mNumParts
)

// NOT IPv6-compatible.
func getPort(addr string) uint64 {
	parts := strings.Split(addr, ":", -1)
	port, err := strconv.Btoui64(parts[len(parts) - 1], 10)
	if err != nil {
		fmt.Printf("error getting port from %q\n", addr)
	}
	return port
}

func RecvUdp(conn net.PacketConn, ch chan paxos.Msg) {
	for {
		pkt := make([]byte, 3000) // make sure it's big enough
		n, addr, err := conn.ReadFrom(pkt)
		if err != nil {
			fmt.Println(err)
			continue
		}
		msg := parse(string(pkt[0:n]))
		msg.From = getPort(addr.String())
		ch <- msg
	}
}

func parse(s string) paxos.Msg {
	parts := strings.Split(s, ":", mNumParts)
	if len(parts) != mNumParts {
		panic(s)
	}

	from, err := strconv.Btoui64(parts[mFrom], 10)
	if err != nil {
		panic(s)
	}

	var to uint64
	if parts[mTo] == "*" {
		to = 0
	} else {
		to, err = strconv.Btoui64(parts[mTo], 10)
		if err != nil {
			panic(err)
		}
	}

	return paxos.Msg{1, from, to, parts[mCmd], parts[mBody]}
}

type FuncPutter func (paxos.Msg)

func (f FuncPutter) Put(m paxos.Msg) {
	f(m)
}

func printMsg(m paxos.Msg) {
	fmt.Printf("should send %v\n", m)
}

func NewUdpPutter(me uint64, addrs []net.Addr, conn net.PacketConn) paxos.Putter {
	put := func(m paxos.Msg) {
		pkt := fmt.Sprintf("%d:%d:%s:%s", me, m.To, m.Cmd, m.Body)
		fmt.Printf("send udp packet %q\n", pkt)
		b := []byte(pkt)
		var to []net.Addr
		if m.To == 0 {
			to = addrs
		} else {
			to = []net.Addr{&net.UDPAddr{net.ParseIP("127.0.0.1"), int(m.To)}}
		}

		for _, addr := range to {
			n, err := conn.WriteTo(b, addr)
			if err != nil {
				fmt.Println(err)
				continue
			}
			if n != len(b) {
				fmt.Printf("sent <%d> bytes, wanted to send <%d>\n", n, len(b))
				continue
			}
		}
	}
	return FuncPutter(put)
}

type Node struct {
	id string
	listenAddr string
	logger *log.Logger
	nodes []net.Addr
	store *store.Store
	manager *paxos.Manager
}

func New(id string, listenAddr string, logger *log.Logger) *Node {
	if id == "" {
		b := make([]byte, 8)
		util.RandBytes(b)
		id = fmt.Sprintf("%x", b)
	}
	return &Node{
		listenAddr:listenAddr,
		logger:logger,
		store:store.New(logger),
		id:id,
	}
}

func (n *Node) Init() {
	var basePort int
	var err os.Error
	basePort, err = strconv.Atoi((n.listenAddr)[1:])
	n.nodes = make([]net.Addr, 5)
	if err != nil {
		fmt.Println(err)
		return
	}
	n.nodes[0] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 0}
	n.nodes[1] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 1}
	n.nodes[2] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 2}
	n.nodes[3] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 3}
	n.nodes[4] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 4}

	nodeKey := "/node/" + n.id
	mut, err := store.EncodeSet(nodeKey, n.listenAddr)
	if err != nil {
		panic(err)
	}
	n.store.Apply(1, mut)
	n.logger.Logf("registered %s at %s\n", n.id, n.listenAddr)
	n.manager = paxos.NewManager(2, uint64(len(n.nodes)), n.logger)
}

// TODO this function should take only an address and get all necessary info
// from the other existing nodes.
func (n *Node) Join(master string) {
	parts := strings.Split(master, "=", 2)
	if len(parts) < 2 {
		panic(fmt.Sprintf("bad master address: %s", master))
	}
	mid, addr := parts[0], parts[1]

	var basePort int
	var err os.Error
	n.nodes = make([]net.Addr, 5)
	basePort, err = strconv.Atoi((addr)[1:])
	if err != nil {
		fmt.Println(err)
		return
	}
	n.nodes[0] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 0}
	n.nodes[1] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 1}
	n.nodes[2] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 2}
	n.nodes[3] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 3}
	n.nodes[4] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 4}
	n.logger.Logf("attempting to attach to %v\n", n.nodes)

	n.logger.Logf("TODO: get a snapshot")
	// TODO remove all this fake stuff and talk to the other nodes
	// BEGIN FAKE STUFF
	nodeKey := "/node/" + mid
	mut, err := store.EncodeSet(nodeKey, addr)
	if err != nil {
		panic(err)
	}
	n.store.Apply(1, mut)
	// END OF FAKE STUFF

	n.manager = paxos.NewManager(2, uint64(len(n.nodes)), n.logger)
}

func (n *Node) RunForever() {
	me, err := strconv.Btoui64((n.listenAddr)[1:], 10)
	if err != nil {
		fmt.Println(err)
		return
	}

	n.logger.Logf("attempting to listen on %s\n", n.listenAddr)

	//open tcp sock

	conn, err := net.ListenPacket("udp", n.listenAddr)
	if err != nil {
		fmt.Println(err)
		return
	}

	udpCh := make(chan paxos.Msg)
	go RecvUdp(conn, udpCh)
	udpPutter := NewUdpPutter(me, n.nodes, conn)

	//n.manager.Init(FuncPutter(printMsg))
	n.manager.Init(udpPutter)

	go func() {
		for pkt := range udpCh {
			fmt.Printf("got udp packet: %#v\n", pkt)
			n.manager.Put(pkt)
		}
	}()

	//go func() {
	//	for m from a client:
	//		switch m.type {
	//		case 'set':
	//			go func() {
	//				v := n.manager.propose(encode(m))
	//				if v == m {
	//					reply 'OK'
	//				} else {
	//					reply 'fail'
	//				}
	//			}()
	//		case 'get':
	//			read from store
	//			return value
	//		}
	//}()

	for {
		n.store.Apply(n.manager.Recv())
	}
}
<commit_msg>add seqn to udp packets<commit_after>package borg

// Think of borg events like inotify events.  We're interested in changes to
// them all.  All events are sent to your instance of borg.  The filtering
// is done once they've arrived.  Let's make keys look like directories to
// aid this comparison.
//
// Example spec:
//
//     /<slug_id>/proc/<type>/<upid>/...
//
// Example interactive session:
//
//     $ borgd -i -a :9999
//     >> ls /proc/123_a3c_a12b3c45/beanstalkd/12345/
//     exe
//     env
//     lock
//     >> cat /proc/123_a3c_a12b3c45/beanstalkd/12345/*
//     beanstalkd -l 0.0.0.0 -p 4563
//     PORT=4563
//     123.4.5.678:9999
//     >>
//
// Example code:
//
//     me, err := borg.ListenAndServe(listenAddr)
//     if err != nil {
//         log.Exitf("listen failed: %v", err)
//     }
//
//     // Handle a specific type of key notification.
//     // The : signals a named variable part.
//     me.HandleFunc(
//         "/proc/:slug/beanstalkd/:upid/lock",
//         func (msg *borg.Message) {
//             if msg.Value == myId {
//                 cmd := beanstalkd ....
//                 ... launch beanstalkd ...
//                 me.Echo(cmd, "/proc/<slug>/beanstalkd/<upid>/cmd")
//             }
//         },
//     )

import (
	"borg/paxos"
	"borg/store"
	"borg/util"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
)

const (
	mSeqn = iota
	mFrom
	mTo
	mCmd
	mBody
	mNumParts
)

// NOT IPv6-compatible.
func getPort(addr string) uint64 {
	parts := strings.Split(addr, ":", -1)
	port, err := strconv.Btoui64(parts[len(parts) - 1], 10)
	if err != nil {
		fmt.Printf("error getting port from %q\n", addr)
	}
	return port
}

func RecvUdp(conn net.PacketConn, ch chan paxos.Msg) {
	for {
		pkt := make([]byte, 3000) // make sure it's big enough
		n, addr, err := conn.ReadFrom(pkt)
		if err != nil {
			fmt.Println(err)
			continue
		}
		msg := parse(string(pkt[0:n]))
		msg.From = getPort(addr.String())
		ch <- msg
	}
}

func parse(s string) paxos.Msg {
	parts := strings.Split(s, ":", mNumParts)
	if len(parts) != mNumParts {
		panic(s)
	}

	seqn, err := strconv.Btoui64(parts[mSeqn], 10)
	if err != nil {
		panic(s)
	}

	from, err := strconv.Btoui64(parts[mFrom], 10)
	if err != nil {
		panic(s)
	}

	var to uint64
	if parts[mTo] == "*" {
		to = 0
	} else {
		to, err = strconv.Btoui64(parts[mTo], 10)
		if err != nil {
			panic(err)
		}
	}

	return paxos.Msg{seqn, from, to, parts[mCmd], parts[mBody]}
}

type FuncPutter func (paxos.Msg)

func (f FuncPutter) Put(m paxos.Msg) {
	f(m)
}

func printMsg(m paxos.Msg) {
	fmt.Printf("should send %v\n", m)
}

func NewUdpPutter(me uint64, addrs []net.Addr, conn net.PacketConn) paxos.Putter {
	put := func(m paxos.Msg) {
		pkt := fmt.Sprintf("%d:%d:%d:%s:%s", m.Seqn, me, m.To, m.Cmd, m.Body)
		fmt.Printf("send udp packet %q\n", pkt)
		b := []byte(pkt)
		var to []net.Addr
		if m.To == 0 {
			to = addrs
		} else {
			to = []net.Addr{&net.UDPAddr{net.ParseIP("127.0.0.1"), int(m.To)}}
		}

		for _, addr := range to {
			n, err := conn.WriteTo(b, addr)
			if err != nil {
				fmt.Println(err)
				continue
			}
			if n != len(b) {
				fmt.Printf("sent <%d> bytes, wanted to send <%d>\n", n, len(b))
				continue
			}
		}
	}
	return FuncPutter(put)
}

type Node struct {
	id string
	listenAddr string
	logger *log.Logger
	nodes []net.Addr
	store *store.Store
	manager *paxos.Manager
}

func New(id string, listenAddr string, logger *log.Logger) *Node {
	if id == "" {
		b := make([]byte, 8)
		util.RandBytes(b)
		id = fmt.Sprintf("%x", b)
	}
	return &Node{
		listenAddr:listenAddr,
		logger:logger,
		store:store.New(logger),
		id:id,
	}
}

func (n *Node) Init() {
	var basePort int
	var err os.Error
	basePort, err = strconv.Atoi((n.listenAddr)[1:])
	n.nodes = make([]net.Addr, 5)
	if err != nil {
		fmt.Println(err)
		return
	}
	n.nodes[0] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 0}
	n.nodes[1] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 1}
	n.nodes[2] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 2}
	n.nodes[3] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 3}
	n.nodes[4] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 4}

	nodeKey := "/node/" + n.id
	mut, err := store.EncodeSet(nodeKey, n.listenAddr)
	if err != nil {
		panic(err)
	}
	n.store.Apply(1, mut)
	n.logger.Logf("registered %s at %s\n", n.id, n.listenAddr)
	n.manager = paxos.NewManager(2, uint64(len(n.nodes)), n.logger)
}

// TODO this function should take only an address and get all necessary info
// from the other existing nodes.
func (n *Node) Join(master string) {
	parts := strings.Split(master, "=", 2)
	if len(parts) < 2 {
		panic(fmt.Sprintf("bad master address: %s", master))
	}
	mid, addr := parts[0], parts[1]

	var basePort int
	var err os.Error
	n.nodes = make([]net.Addr, 5)
	basePort, err = strconv.Atoi((addr)[1:])
	if err != nil {
		fmt.Println(err)
		return
	}
	n.nodes[0] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 0}
	n.nodes[1] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 1}
	n.nodes[2] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 2}
	n.nodes[3] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 3}
	n.nodes[4] = &net.UDPAddr{net.ParseIP("127.0.0.1"), basePort + 4}
	n.logger.Logf("attempting to attach to %v\n", n.nodes)

	n.logger.Logf("TODO: get a snapshot")
	// TODO remove all this fake stuff and talk to the other nodes
	// BEGIN FAKE STUFF
	nodeKey := "/node/" + mid
	mut, err := store.EncodeSet(nodeKey, addr)
	if err != nil {
		panic(err)
	}
	n.store.Apply(1, mut)
	// END OF FAKE STUFF

	n.manager = paxos.NewManager(2, uint64(len(n.nodes)), n.logger)
}

func (n *Node) RunForever() {
	me, err := strconv.Btoui64((n.listenAddr)[1:], 10)
	if err != nil {
		fmt.Println(err)
		return
	}

	n.logger.Logf("attempting to listen on %s\n", n.listenAddr)

	//open tcp sock

	conn, err := net.ListenPacket("udp", n.listenAddr)
	if err != nil {
		fmt.Println(err)
		return
	}

	udpCh := make(chan paxos.Msg)
	go RecvUdp(conn, udpCh)
	udpPutter := NewUdpPutter(me, n.nodes, conn)

	//n.manager.Init(FuncPutter(printMsg))
	n.manager.Init(udpPutter)

	go func() {
		for pkt := range udpCh {
			fmt.Printf("got udp packet: %#v\n", pkt)
			n.manager.Put(pkt)
		}
	}()

	//go func() {
	//	for m from a client:
	//		switch m.type {
	//		case 'set':
	//			go func() {
	//				v := n.manager.propose(encode(m))
	//				if v == m {
	//					reply 'OK'
	//				} else {
	//					reply 'fail'
	//				}
	//			}()
	//		case 'get':
	//			read from store
	//			return value
	//		}
	//}()

	for {
		n.store.Apply(n.manager.Recv())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math/rand"
	"time"
)

type Song struct {
	Id      int    `json:"id"`
	Videoid string `json:"videoid"`
	Name    string `json:"name"`
	Length  int    `json:"length"`
	Seek    int    `json:"seek"`
}

type Playlist []Song

func (s *Song) init(id int, videoid string, name string, length int, seek int) Song {
	return Song{
		Id:      id,
		Videoid: videoid,
		Name:    name,
		Length:  length,
		Seek:    seek,
	}
}

func createSong(videoid string, name string) Song {
	return Song{
		Id:      -1,
		Videoid: videoid,
		Name:    name,
		Length:  getDuration(videoid),
		Seek:    -5,
	}
}

func Truncate() {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("TRUNCATE playlist")
	CheckError(err)
	_, err = stmt.Exec()
	CheckError(err)
}

func cleanup(results []Song) []Song {
	var cleanedResults []Song
	for i := range results {
		if results[i].Length != -1 {
			cleanedResults = append(cleanedResults, results[i])
		}
	}
	return cleanedResults
}

func Seed() {
	seedQuery := "tum se hi"
	searchResults := cleanup(Search(seedQuery))
	seedSong := searchResults[0]
	Truncate()
	enqueue(seedSong)
}

func GetPlaylist() Playlist {
	var id int
	var videoid string
	var name string
	var length int
	var seek int
	playlist := []Song{}
	db := GetDbHandle()
	defer db.Close()
	rows, err := db.Query("SELECT  * from  playlist order by id")
	CheckError(err)
	defer rows.Close()
	for rows.Next() {
		err := rows.Scan(&id, &videoid, &name, &length, &seek)
		var s = Song{}
		s = s.init(id, videoid, name, length, seek)
		CheckError(err)
		playlist = append(playlist, s)
	}
	return playlist
}

func CurrentlyPlaying() Song {
	var id int
	var videoid string
	var name string
	var length int
	var seek int
	db := GetDbHandle()
	defer db.Close()
	err := db.QueryRow("SELECT * FROM playlist ORDER BY id ASC LIMIT 1").Scan(&id, &videoid, &name, &length, &seek)
	CheckError(err)
	var s = Song{}
	s = s.init(id, videoid, name, length, seek)
	return s
}

func updateSeek(s Song) {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("UPDATE playlist SET seek = seek + 1 WHERE id = ?")
	CheckError(err)
	_, err = stmt.Exec(s.Id)
	CheckError(err)
}

func getLastSong() Song {
	var id int
	var videoid string
	var name string
	var length int
	var seek int
	db := GetDbHandle()
	defer db.Close()
	err := db.QueryRow("SELECT * FROM playlist ORDER BY id DESC LIMIT 1").Scan(&id, &videoid, &name, &length, &seek)
	CheckError(err)
	var s = Song{}
	lastSong := s.init(id, videoid, name, length, seek)
	return lastSong
}

func enqueue(s Song) {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("INSERT INTO playlist (videoid, name, length, seek) VALUES (?, ?, ?, ?)")
	CheckError(err)
	_, err = stmt.Exec(s.Videoid, s.Name, s.Length, s.Seek)
	CheckError(err)
}

func remove(s Song) {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("DELETE FROM playlist WHERE id = ?")
	CheckError(err)
	_, err = stmt.Exec(s.Id)
	CheckError(err)
}

func Size() int {
	var size int
	db := GetDbHandle()
	defer db.Close()
	err := db.QueryRow("SELECT count(*) FROM playlist").Scan(&size)
	CheckError(err)
	return size
}

func Add(query string) {
	searchResults := cleanup(Search(query))
	enqueue(searchResults[0])
}

func autoAdd() {
	ticker := time.NewTicker(time.Second * 5)
	for _ = range ticker.C {
		c := CurrentlyPlaying()
		timeRemaining := c.Length - c.Seek
		if Size() == 1 && timeRemaining < 30 {
			newSong := recommend(getLastSong())
			enqueue(newSong)
		}
	}
}

func recommend(s Song) Song {
	var recommendedSong Song
	recommendations := cleanup(Recommend(s.Videoid))
	if len(recommendations) < 6 {
		seedQuery := "tum se hi"
		searchResults := cleanup(Search(seedQuery))
		recommendedSong = searchResults[0]
	} else {
		songindex := rand.Intn(len(recommendations)-3) + 3
		recommendedSong = recommendations[songindex]
	}
	return recommendedSong
}

func Refresh() {
	s := CurrentlyPlaying()
	if s.Seek < s.Length {
		updateSeek(s)
		fmt.Println(s.Videoid, "   ", s.Seek, "          ", GetPlaylist())
	} else {
		remove(s)
		go PostToSlack("#nowplaying " + CurrentlyPlaying().Name)
		Refresh()
	}
}

func Skip() {
	s := CurrentlyPlaying()
	PostToSlack("#skipped " + CurrentlyPlaying().Name + ". Don't do this :rage:")
	remove(s)
	go PostToSlack("#nowplaying " + CurrentlyPlaying().Name)
	Refresh()
}
<commit_msg>adding added_by attribute to each song<commit_after>package main

import (
	"fmt"
	"math/rand"
	"time"
)

type Song struct {
	Id      int    `json:"id"`
	Videoid string `json:"videoid"`
	Name    string `json:"name"`
	Length  int    `json:"length"`
	Seek    int    `json:"seek"`
}

type Playlist []Song

func (s *Song) init(id int, videoid string, name string, length int, seek int) Song {
	return Song{
		Id:      id,
		Videoid: videoid,
		Name:    name,
		Length:  length,
		Seek:    seek,
	}
}

func createSong(videoid string, name string) Song {
	return Song{
		Id:      -1,
		Videoid: videoid,
		Name:    name,
		Length:  getDuration(videoid),
		Seek:    -5,
	}
}

func Truncate() {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("TRUNCATE playlist")
	CheckError(err)
	_, err = stmt.Exec()
	CheckError(err)
}

func cleanup(results []Song) []Song {
	var cleanedResults []Song
	for i := range results {
		if results[i].Length != -1 {
			cleanedResults = append(cleanedResults, results[i])
		}
	}
	return cleanedResults
}

func Seed() {
	seedQuery := "tum se hi"
	searchResults := cleanup(Search(seedQuery))
	seedSong := searchResults[0]
	Truncate()
	enqueue(seedSong, "system")
}

func GetPlaylist() Playlist {
	var id int
	var videoid string
	var name string
	var length int
	var seek int
	var addedBy string
	playlist := []Song{}
	db := GetDbHandle()
	defer db.Close()
	rows, err := db.Query("SELECT  * from  playlist order by id")
	CheckError(err)
	defer rows.Close()
	for rows.Next() {
		err := rows.Scan(&id, &videoid, &name, &length, &seek, &addedBy)
		var s = Song{}
		s = s.init(id, videoid, name, length, seek)
		CheckError(err)
		playlist = append(playlist, s)
	}
	return playlist
}

func CurrentlyPlaying() Song {
	var id int
	var videoid string
	var name string
	var length int
	var seek int
	var addedBy string
	db := GetDbHandle()
	defer db.Close()
	err := db.QueryRow("SELECT * FROM playlist ORDER BY id ASC LIMIT 1").Scan(&id, &videoid, &name, &length, &seek, &addedBy)
	CheckError(err)
	var s = Song{}
	s = s.init(id, videoid, name, length, seek)
	return s
}

func updateSeek(s Song) {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("UPDATE playlist SET seek = seek + 1 WHERE id = ?")
	CheckError(err)
	_, err = stmt.Exec(s.Id)
	CheckError(err)
}

func getLastSong() Song {
	var id int
	var videoid string
	var name string
	var length int
	var seek int
	db := GetDbHandle()
	defer db.Close()
	err := db.QueryRow("SELECT * FROM playlist ORDER BY id DESC LIMIT 1").Scan(&id, &videoid, &name, &length, &seek)
	CheckError(err)
	var s = Song{}
	lastSong := s.init(id, videoid, name, length, seek)
	return lastSong
}

func enqueue(s Song, agent string) {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("INSERT INTO playlist (videoid, name, length, seek, added_by) VALUES (?, ?, ?, ?, ?)")
	CheckError(err)
	_, err = stmt.Exec(s.Videoid, s.Name, s.Length, s.Seek, agent)
	CheckError(err)
}

func remove(s Song) {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("DELETE FROM playlist WHERE id = ?")
	CheckError(err)
	_, err = stmt.Exec(s.Id)
	CheckError(err)
}

func Size() int {
	var size int
	db := GetDbHandle()
	defer db.Close()
	err := db.QueryRow("SELECT count(*) FROM playlist").Scan(&size)
	CheckError(err)
	return size
}

func Add(query string) {
	searchResults := cleanup(Search(query))
	enqueue(searchResults[0], "client")
}

func autoAdd() {
	ticker := time.NewTicker(time.Second * 5)
	for _ = range ticker.C {
		c := CurrentlyPlaying()
		timeRemaining := c.Length - c.Seek
		if Size() == 1 && timeRemaining < 30 {
			newSong := recommend(getLastSong())
			enqueue(newSong, "system")
		}
	}
}

func recommend(s Song) Song {
	var recommendedSong Song
	recommendations := cleanup(Recommend(s.Videoid))
	if len(recommendations) < 6 {
		seedQuery := "tum se hi"
		searchResults := cleanup(Search(seedQuery))
		recommendedSong = searchResults[0]
	} else {
		songindex := rand.Intn(len(recommendations)-3) + 3
		recommendedSong = recommendations[songindex]
	}
	return recommendedSong
}

func Refresh() {
	s := CurrentlyPlaying()
	if s.Seek < s.Length {
		updateSeek(s)
		fmt.Println(s.Videoid, "   ", s.Seek, "          ", GetPlaylist())
	} else {
		remove(s)
		go PostToSlack("#nowplaying " + CurrentlyPlaying().Name)
		Refresh()
	}
}

func Skip() {
	s := CurrentlyPlaying()
	PostToSlack("#skipped " + CurrentlyPlaying().Name + ". Don't do this :rage:")
	remove(s)
	go PostToSlack("#nowplaying " + CurrentlyPlaying().Name)
	Refresh()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/grafov/m3u8"
	"gopkg.in/redis.v1"
	"log"
	"net/http"
	"os/exec"
	"time"
)

var broadcastCursor = make(chan int)
var currentPlaylist string
var client *redis.Client

func init() {
	client = redis.NewTCPClient(&redis.Options{
		Addr: "localhost:6379",
	})

	pong, err := client.Ping().Result()
	log.Println(pong, err)
}

type PlaylistGenerator struct {
	cursor chan int
}

func (pl PlaylistGenerator) VideoFileForSequence(seq int) string {
	generated := fmt.Sprintf("http://www.smick.tv/media/truedetectives2e1movie2m%05d.ts", seq)
	return generated
}

func (pl PlaylistGenerator) GeneratedVideoFileForSequence(seq int) string {
	prefix := ""
	pref := client.Get("broadcast-prefix").Val()
	prefix = pref

	generated := fmt.Sprintf("fileSequence%d.ts", seq)
	postProcess := fmt.Sprintf("fileSequence%d-post.ts", seq)
	sourceVideo := prefix + generated
	destVideo := prefix + postProcess

	currentTime := time.Now().Format("3:04 PM")

	twoClipsAgo := seq - 2
	if twoClipsAgo > 0 {
		mapKey := fmt.Sprintf("/fileSequence%d-post.ts", twoClipsAgo)
		log.Println("map key is", mapKey)
		if count, ok := lfs.Counter[mapKey]; ok {
			currentTime = fmt.Sprintf("%d active viewers", count)
		}
	}

	err := RenderTextToPNG(currentTime, "time.png")
	if err == nil {
		cmd := exec.Command("avconv", "-i", sourceVideo, "-vf", "movie=time.png [watermark];[in][watermark] overlay=0:0 [out]", "-y", "-map", "0", "-c:a", "copy", "-c:v", "mpeg2video", "-an", destVideo)
		err := cmd.Start()
		if err != nil {
			return sourceVideo
		}
		err = cmd.Wait()
		return destVideo
	}

	return sourceVideo
}

func (pl *PlaylistGenerator) KeepPlaylistUpdated() {
	p, e := m3u8.NewMediaPlaylist(1000, 1000)
	if e != nil {
		log.Println("Error creating media playlist:", e)
		return
	}
	currentPlaylist = p.Encode().String()

	for seqnum := 1; seqnum < 1854; seqnum = <-pl.cursor {
		videoFile := pl.VideoFileForSequence(seqnum)
		if err := p.Append(videoFile, 5.0, ""); err != nil {
			log.Println("Error appending item to playlist:", err, fmt.Sprintf("movie2m%5d.ts", seqnum))
		}
		currentPlaylist = p.Encode().String()
	}
}

func (pl *PlaylistGenerator) Start() {
	pl.cursor = make(chan int, 1000)

	go pl.KeepPlaylistUpdated()
	for i := 1; i < 1854; i++ {
		log.Println(i)
		pl.cursor <- i
		time.Sleep(5 * time.Second)
	}
}

func (pl PlaylistGenerator) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, currentPlaylist)
}
<commit_msg>fixed url<commit_after>package main

import (
	"fmt"
	"github.com/grafov/m3u8"
	"gopkg.in/redis.v1"
	"log"
	"net/http"
	"os/exec"
	"time"
)

var broadcastCursor = make(chan int)
var currentPlaylist string
var client *redis.Client

func init() {
	client = redis.NewTCPClient(&redis.Options{
		Addr: "localhost:6379",
	})

	pong, err := client.Ping().Result()
	log.Println(pong, err)
}

type PlaylistGenerator struct {
	cursor chan int
}

func (pl PlaylistGenerator) VideoFileForSequence(seq int) string {
	generated := fmt.Sprintf("http://www.smick.tv/media/truedetectives2e1movie%05d.ts", seq)
	return generated
}

func (pl PlaylistGenerator) GeneratedVideoFileForSequence(seq int) string {
	prefix := ""
	pref := client.Get("broadcast-prefix").Val()
	prefix = pref

	generated := fmt.Sprintf("fileSequence%d.ts", seq)
	postProcess := fmt.Sprintf("fileSequence%d-post.ts", seq)
	sourceVideo := prefix + generated
	destVideo := prefix + postProcess

	currentTime := time.Now().Format("3:04 PM")

	twoClipsAgo := seq - 2
	if twoClipsAgo > 0 {
		mapKey := fmt.Sprintf("/fileSequence%d-post.ts", twoClipsAgo)
		log.Println("map key is", mapKey)
		if count, ok := lfs.Counter[mapKey]; ok {
			currentTime = fmt.Sprintf("%d active viewers", count)
		}
	}

	err := RenderTextToPNG(currentTime, "time.png")
	if err == nil {
		cmd := exec.Command("avconv", "-i", sourceVideo, "-vf", "movie=time.png [watermark];[in][watermark] overlay=0:0 [out]", "-y", "-map", "0", "-c:a", "copy", "-c:v", "mpeg2video", "-an", destVideo)
		err := cmd.Start()
		if err != nil {
			return sourceVideo
		}
		err = cmd.Wait()
		return destVideo
	}

	return sourceVideo
}

func (pl *PlaylistGenerator) KeepPlaylistUpdated() {
	p, e := m3u8.NewMediaPlaylist(1000, 1000)
	if e != nil {
		log.Println("Error creating media playlist:", e)
		return
	}
	currentPlaylist = p.Encode().String()

	for seqnum := 1; seqnum < 1854; seqnum = <-pl.cursor {
		videoFile := pl.VideoFileForSequence(seqnum)
		if err := p.Append(videoFile, 5.0, ""); err != nil {
			log.Println("Error appending item to playlist:", err, fmt.Sprintf("movie2m%5d.ts", seqnum))
		}
		currentPlaylist = p.Encode().String()
	}
}

func (pl *PlaylistGenerator) Start() {
	pl.cursor = make(chan int, 1000)

	go pl.KeepPlaylistUpdated()
	for i := 1; i < 1854; i++ {
		log.Println(i)
		pl.cursor <- i
		time.Sleep(5 * time.Second)
	}
}

func (pl PlaylistGenerator) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, currentPlaylist)
}
<|endoftext|>


<commit_before>package cli

import (
	"context"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/kopia/kopia/repo"
	"github.com/kopia/kopia/snapshot"
	"github.com/kopia/kopia/snapshot/policy"
	"github.com/kopia/kopia/snapshot/snapshotfs"
)

const (
	maxSnapshotDescriptionLength = 1024
	timeFormat                   = "2006-01-02 15:04:05 MST"
)

var (
	snapshotCreateCommand = snapshotCommands.Command("create", "Creates a snapshot of local directory or file.").Default()

	snapshotCreateSources                 = snapshotCreateCommand.Arg("source", "Files or directories to create snapshot(s) of.").ExistingFilesOrDirs()
	snapshotCreateAll                     = snapshotCreateCommand.Flag("all", "Create snapshots for files or directories previously backed up by this user on this computer").Bool()
	snapshotCreateCheckpointUploadLimitMB = snapshotCreateCommand.Flag("upload-limit-mb", "Stop the backup process after the specified amount of data (in MB) has been uploaded.").PlaceHolder("MB").Default("0").Int64()
	snapshotCreateDescription             = snapshotCreateCommand.Flag("description", "Free-form snapshot description.").String()
	snapshotCreateForceHash               = snapshotCreateCommand.Flag("force-hash", "Force hashing of source files for a given percentage of files [0..100]").Default("0").Int()
	snapshotCreateParallelUploads         = snapshotCreateCommand.Flag("parallel", "Upload N files in parallel").PlaceHolder("N").Default("0").Int()
	snapshotCreateHostname                = snapshotCreateCommand.Flag("hostname", "Override local hostname.").String()
	snapshotCreateUsername                = snapshotCreateCommand.Flag("username", "Override local username.").String()
	snapshotCreateStartTime               = snapshotCreateCommand.Flag("start-time", "Override snapshot start timestamp.").String()
	snapshotCreateEndTime                 = snapshotCreateCommand.Flag("end-time", "Override snapshot end timestamp.").String()
)

func runBackupCommand(ctx context.Context, rep repo.Repository) error {
	sources := *snapshotCreateSources

	if *snapshotCreateAll {
		local, err := getLocalBackupPaths(ctx, rep)
		if err != nil {
			return err
		}

		sources = append(sources, local...)
	}

	if len(sources) == 0 {
		return errors.New("no backup sources")
	}

	u := snapshotfs.NewUploader(rep)
	u.MaxUploadBytes = *snapshotCreateCheckpointUploadLimitMB << 20 //nolint:gomnd
	u.ForceHashPercentage = *snapshotCreateForceHash
	u.ParallelUploads = *snapshotCreateParallelUploads
	onCtrlC(u.Cancel)

	u.Progress = progress

	startTime, err := parseTimestamp(*snapshotCreateStartTime)
	if err != nil {
		return errors.Wrap(err, "could not parse start-time")
	}

	endTime, err := parseTimestamp(*snapshotCreateEndTime)
	if err != nil {
		return errors.Wrap(err, "could not parse end-time")
	}

	if startTimeAfterEndTime(startTime, endTime) {
		return errors.New("start time override cannot be after the end time override")
	}

	if len(*snapshotCreateDescription) > maxSnapshotDescriptionLength {
		return errors.New("description too long")
	}

	var finalErrors []string

	for _, snapshotDir := range sources {
		if u.IsCancelled() {
			printStderr("Upload canceled\n")
			break
		}

		dir, err := filepath.Abs(snapshotDir)
		if err != nil {
			return errors.Errorf("invalid source: '%s': %s", snapshotDir, err)
		}

		sourceInfo := snapshot.SourceInfo{
			Path:     filepath.Clean(dir),
			Host:     rep.Hostname(),
			UserName: rep.Username(),
		}

		if h := *snapshotCreateHostname; h != "" {
			sourceInfo.Host = h
		}

		if u := *snapshotCreateUsername; u != "" {
			sourceInfo.UserName = u
		}

		if err := snapshotSingleSource(ctx, rep, u, sourceInfo); err != nil {
			finalErrors = append(finalErrors, err.Error())
		}
	}

	if len(finalErrors) == 0 {
		return nil
	}

	return errors.Errorf("encountered %v errors:\n%v", len(finalErrors), strings.Join(finalErrors, "\n"))
}

func parseTimestamp(timestamp string) (time.Time, error) {
	if timestamp != "" {
		parsedTimestamp, err := time.Parse(timeFormat, timestamp)

		if err != nil {
			return time.Time{}, err
		}

		return parsedTimestamp, nil
	}

	return time.Time{}, nil
}

func startTimeAfterEndTime(startTime, endTime time.Time) bool {
	return !startTime.IsZero() &&
		!endTime.IsZero() &&
		startTime.After(endTime)
}

func snapshotSingleSource(ctx context.Context, rep repo.Repository, u *snapshotfs.Uploader, sourceInfo snapshot.SourceInfo) error {
	printStderr("Snapshotting %v ...\n", sourceInfo)

	t0 := time.Now()

	localEntry, err := getLocalFSEntry(ctx, sourceInfo.Path)
	if err != nil {
		return errors.Wrap(err, "unable to get local filesystem entry")
	}

	previous, err := findPreviousSnapshotManifest(ctx, rep, sourceInfo, nil)
	if err != nil {
		return err
	}

	policyTree, err := policy.TreeForSource(ctx, rep, sourceInfo)
	if err != nil {
		return errors.Wrap(err, "unable to get policy tree")
	}

	log(ctx).Debugf("uploading %v using %v previous manifests", sourceInfo, len(previous))

	manifest, err := u.Upload(ctx, localEntry, policyTree, sourceInfo, previous...)
	if err != nil {
		return err
	}

	manifest.Description = *snapshotCreateDescription

	duration := manifest.EndTime.Sub(manifest.StartTime)
	inverseDuration := manifest.StartTime.Sub(manifest.EndTime)

	startTimeOverride, _ := parseTimestamp(*snapshotCreateStartTime)
	endTimeOverride, _ := parseTimestamp(*snapshotCreateEndTime)

	if !startTimeOverride.IsZero() {
		manifest.StartTime = startTimeOverride

		if endTimeOverride.IsZero() {
			// Calculate the correct end time based on current duration if they're not specified
			manifest.EndTime = startTimeOverride.Add(duration)
		}
	}

	if !endTimeOverride.IsZero() {
		manifest.EndTime = endTimeOverride

		if startTimeOverride.IsZero() {
			manifest.StartTime = endTimeOverride.Add(inverseDuration)
		}
	}

	snapID, err := snapshot.SaveSnapshot(ctx, rep, manifest)
	if err != nil {
		return errors.Wrap(err, "cannot save manifest")
	}

	if _, err = policy.ApplyRetentionPolicy(ctx, rep, sourceInfo, true); err != nil {
		return errors.Wrap(err, "unable to apply retention policy")
	}

	if ferr := rep.Flush(ctx); ferr != nil {
		return errors.Wrap(ferr, "flush error")
	}

	progress.Finish()

	var maybePartial string
	if manifest.IncompleteReason != "" {
		maybePartial = " partial"
	}

	if ds := manifest.RootEntry.DirSummary; ds != nil {
		if ds.NumFailed > 0 {
			errorColor.Fprintf(os.Stderr, "\nIgnored %v errors while snapshotting.", ds.NumFailed) //nolint:errcheck
		}
	}

	printStderr("\nCreated%v snapshot with root %v and ID %v in %v\n", maybePartial, manifest.RootObjectID(), snapID, time.Since(t0).Truncate(time.Second))

	return err
}

// findPreviousSnapshotManifest returns the list of previous snapshots for a given source, including
// last complete snapshot and possibly some number of incomplete snapshots following it.
func findPreviousSnapshotManifest(ctx context.Context, rep repo.Repository, sourceInfo snapshot.SourceInfo, noLaterThan *time.Time) ([]*snapshot.Manifest, error) {
	man, err := snapshot.ListSnapshots(ctx, rep, sourceInfo)
	if err != nil {
		return nil, errors.Wrap(err, "error listing previous snapshots")
	}

	// phase 1 - find latest complete snapshot.
	var previousComplete *snapshot.Manifest

	var previousCompleteStartTime time.Time

	var result []*snapshot.Manifest

	for _, p := range man {
		if noLaterThan != nil && p.StartTime.After(*noLaterThan) {
			continue
		}

		if p.IncompleteReason == "" && (previousComplete == nil || p.StartTime.After(previousComplete.StartTime)) {
			previousComplete = p
			previousCompleteStartTime = p.StartTime
		}
	}

	if previousComplete != nil {
		result = append(result, previousComplete)
	}

	// add all incomplete snapshots after that
	for _, p := range man {
		if noLaterThan != nil && p.StartTime.After(*noLaterThan) {
			continue
		}

		if p.IncompleteReason != "" && p.StartTime.After(previousCompleteStartTime) {
			result = append(result, p)
		}
	}

	return result, nil
}

func getLocalBackupPaths(ctx context.Context, rep repo.Repository) ([]string, error) {
	log(ctx).Debugf("Looking for previous backups of '%v@%v'...", rep.Hostname(), rep.Username())

	sources, err := snapshot.ListSources(ctx, rep)
	if err != nil {
		return nil, errors.Wrap(err, "unable to list sources")
	}

	var result []string

	for _, src := range sources {
		if src.Host == rep.Hostname() && src.UserName == rep.Username() {
			result = append(result, src.Path)
		}
	}

	return result, nil
}

func init() {
	snapshotCreateCommand.Action(repositoryAction(runBackupCommand))
}
<commit_msg>Minor cleanup for snapshot time override (#392)<commit_after>package cli

import (
	"context"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/kopia/kopia/repo"
	"github.com/kopia/kopia/snapshot"
	"github.com/kopia/kopia/snapshot/policy"
	"github.com/kopia/kopia/snapshot/snapshotfs"
)

const (
	maxSnapshotDescriptionLength = 1024
	timeFormat                   = "2006-01-02 15:04:05 MST"
)

var (
	snapshotCreateCommand = snapshotCommands.Command("create", "Creates a snapshot of local directory or file.").Default()

	snapshotCreateSources                 = snapshotCreateCommand.Arg("source", "Files or directories to create snapshot(s) of.").ExistingFilesOrDirs()
	snapshotCreateAll                     = snapshotCreateCommand.Flag("all", "Create snapshots for files or directories previously backed up by this user on this computer").Bool()
	snapshotCreateCheckpointUploadLimitMB = snapshotCreateCommand.Flag("upload-limit-mb", "Stop the backup process after the specified amount of data (in MB) has been uploaded.").PlaceHolder("MB").Default("0").Int64()
	snapshotCreateDescription             = snapshotCreateCommand.Flag("description", "Free-form snapshot description.").String()
	snapshotCreateForceHash               = snapshotCreateCommand.Flag("force-hash", "Force hashing of source files for a given percentage of files [0..100]").Default("0").Int()
	snapshotCreateParallelUploads         = snapshotCreateCommand.Flag("parallel", "Upload N files in parallel").PlaceHolder("N").Default("0").Int()
	snapshotCreateHostname                = snapshotCreateCommand.Flag("hostname", "Override local hostname.").String()
	snapshotCreateUsername                = snapshotCreateCommand.Flag("username", "Override local username.").String()
	snapshotCreateStartTime               = snapshotCreateCommand.Flag("start-time", "Override snapshot start timestamp.").String()
	snapshotCreateEndTime                 = snapshotCreateCommand.Flag("end-time", "Override snapshot end timestamp.").String()
)

func runBackupCommand(ctx context.Context, rep repo.Repository) error {
	sources := *snapshotCreateSources

	if *snapshotCreateAll {
		local, err := getLocalBackupPaths(ctx, rep)
		if err != nil {
			return err
		}

		sources = append(sources, local...)
	}

	if len(sources) == 0 {
		return errors.New("no backup sources")
	}

	u := snapshotfs.NewUploader(rep)
	u.MaxUploadBytes = *snapshotCreateCheckpointUploadLimitMB << 20 //nolint:gomnd
	u.ForceHashPercentage = *snapshotCreateForceHash
	u.ParallelUploads = *snapshotCreateParallelUploads
	onCtrlC(u.Cancel)

	u.Progress = progress

	startTime, err := parseTimestamp(*snapshotCreateStartTime)
	if err != nil {
		return errors.Wrap(err, "could not parse start-time")
	}

	endTime, err := parseTimestamp(*snapshotCreateEndTime)
	if err != nil {
		return errors.Wrap(err, "could not parse end-time")
	}

	if startTimeAfterEndTime(startTime, endTime) {
		return errors.New("start time override cannot be after the end time override")
	}

	if len(*snapshotCreateDescription) > maxSnapshotDescriptionLength {
		return errors.New("description too long")
	}

	var finalErrors []string

	for _, snapshotDir := range sources {
		if u.IsCancelled() {
			printStderr("Upload canceled\n")
			break
		}

		dir, err := filepath.Abs(snapshotDir)
		if err != nil {
			return errors.Errorf("invalid source: '%s': %s", snapshotDir, err)
		}

		sourceInfo := snapshot.SourceInfo{
			Path:     filepath.Clean(dir),
			Host:     rep.Hostname(),
			UserName: rep.Username(),
		}

		if h := *snapshotCreateHostname; h != "" {
			sourceInfo.Host = h
		}

		if u := *snapshotCreateUsername; u != "" {
			sourceInfo.UserName = u
		}

		if err := snapshotSingleSource(ctx, rep, u, sourceInfo); err != nil {
			finalErrors = append(finalErrors, err.Error())
		}
	}

	if len(finalErrors) == 0 {
		return nil
	}

	return errors.Errorf("encountered %v errors:\n%v", len(finalErrors), strings.Join(finalErrors, "\n"))
}

func parseTimestamp(timestamp string) (time.Time, error) {
	if timestamp == "" {
		return time.Time{}, nil
	}

	return time.Parse(timeFormat, timestamp)
}

func startTimeAfterEndTime(startTime, endTime time.Time) bool {
	return !startTime.IsZero() &&
		!endTime.IsZero() &&
		startTime.After(endTime)
}

func snapshotSingleSource(ctx context.Context, rep repo.Repository, u *snapshotfs.Uploader, sourceInfo snapshot.SourceInfo) error {
	printStderr("Snapshotting %v ...\n", sourceInfo)

	t0 := time.Now()

	localEntry, err := getLocalFSEntry(ctx, sourceInfo.Path)
	if err != nil {
		return errors.Wrap(err, "unable to get local filesystem entry")
	}

	previous, err := findPreviousSnapshotManifest(ctx, rep, sourceInfo, nil)
	if err != nil {
		return err
	}

	policyTree, err := policy.TreeForSource(ctx, rep, sourceInfo)
	if err != nil {
		return errors.Wrap(err, "unable to get policy tree")
	}

	log(ctx).Debugf("uploading %v using %v previous manifests", sourceInfo, len(previous))

	manifest, err := u.Upload(ctx, localEntry, policyTree, sourceInfo, previous...)
	if err != nil {
		return err
	}

	manifest.Description = *snapshotCreateDescription
	startTimeOverride, _ := parseTimestamp(*snapshotCreateStartTime)
	endTimeOverride, _ := parseTimestamp(*snapshotCreateEndTime)

	if !startTimeOverride.IsZero() {
		if endTimeOverride.IsZero() {
			// Calculate the correct end time based on current duration if they're not specified
			duration := manifest.EndTime.Sub(manifest.StartTime)
			manifest.EndTime = startTimeOverride.Add(duration)
		}

		manifest.StartTime = startTimeOverride
	}

	if !endTimeOverride.IsZero() {
		if startTimeOverride.IsZero() {
			inverseDuration := manifest.StartTime.Sub(manifest.EndTime)
			manifest.StartTime = endTimeOverride.Add(inverseDuration)
		}

		manifest.EndTime = endTimeOverride
	}

	snapID, err := snapshot.SaveSnapshot(ctx, rep, manifest)
	if err != nil {
		return errors.Wrap(err, "cannot save manifest")
	}

	if _, err = policy.ApplyRetentionPolicy(ctx, rep, sourceInfo, true); err != nil {
		return errors.Wrap(err, "unable to apply retention policy")
	}

	if ferr := rep.Flush(ctx); ferr != nil {
		return errors.Wrap(ferr, "flush error")
	}

	progress.Finish()

	var maybePartial string
	if manifest.IncompleteReason != "" {
		maybePartial = " partial"
	}

	if ds := manifest.RootEntry.DirSummary; ds != nil {
		if ds.NumFailed > 0 {
			errorColor.Fprintf(os.Stderr, "\nIgnored %v errors while snapshotting.", ds.NumFailed) //nolint:errcheck
		}
	}

	printStderr("\nCreated%v snapshot with root %v and ID %v in %v\n", maybePartial, manifest.RootObjectID(), snapID, time.Since(t0).Truncate(time.Second))

	return err
}

// findPreviousSnapshotManifest returns the list of previous snapshots for a given source, including
// last complete snapshot and possibly some number of incomplete snapshots following it.
func findPreviousSnapshotManifest(ctx context.Context, rep repo.Repository, sourceInfo snapshot.SourceInfo, noLaterThan *time.Time) ([]*snapshot.Manifest, error) {
	man, err := snapshot.ListSnapshots(ctx, rep, sourceInfo)
	if err != nil {
		return nil, errors.Wrap(err, "error listing previous snapshots")
	}

	// phase 1 - find latest complete snapshot.
	var previousComplete *snapshot.Manifest

	var previousCompleteStartTime time.Time

	var result []*snapshot.Manifest

	for _, p := range man {
		if noLaterThan != nil && p.StartTime.After(*noLaterThan) {
			continue
		}

		if p.IncompleteReason == "" && (previousComplete == nil || p.StartTime.After(previousComplete.StartTime)) {
			previousComplete = p
			previousCompleteStartTime = p.StartTime
		}
	}

	if previousComplete != nil {
		result = append(result, previousComplete)
	}

	// add all incomplete snapshots after that
	for _, p := range man {
		if noLaterThan != nil && p.StartTime.After(*noLaterThan) {
			continue
		}

		if p.IncompleteReason != "" && p.StartTime.After(previousCompleteStartTime) {
			result = append(result, p)
		}
	}

	return result, nil
}

func getLocalBackupPaths(ctx context.Context, rep repo.Repository) ([]string, error) {
	log(ctx).Debugf("Looking for previous backups of '%v@%v'...", rep.Hostname(), rep.Username())

	sources, err := snapshot.ListSources(ctx, rep)
	if err != nil {
		return nil, errors.Wrap(err, "unable to list sources")
	}

	var result []string

	for _, src := range sources {
		if src.Host == rep.Hostname() && src.UserName == rep.Username() {
			result = append(result, src.Path)
		}
	}

	return result, nil
}

func init() {
	snapshotCreateCommand.Action(repositoryAction(runBackupCommand))
}
<|endoftext|>


<commit_before>// Copyright 2014 Jesper Brodersen. All rights reserved.
// This code is BSD-licensed, see LICENSE file.

// pm-get is a package manger written in Go Language
// this is the user application for running daily tasks on packages,
// like browsing, installing, uninstalling and updating
package main

import (
	"github.com/broeman/pm-tools/cmd" // using CLI command args
	"github.com/codegangsta/cli"
	"os"
	"runtime"
)

const MAN_VER = "0.01 Alpha"

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())
}

func main() {
	app := cli.NewApp()
	app.Name = "pm-tools"
	app.Usage = "Package Manager in Go: Toolkit"
	app.Version = MAN_VER
	app.Commands = []cli.Command{
		cmd.Init,      // initialize database if not exist, placeholder for setup
		cmd.Installed, // shows the installed packages, placeholder for testing
		//  cmd.Setup,	// settings for the user, init db
		// 	cmd.AddPackage,    // adding a package to the database
		// 	cmd.ShowPackage,   // showing a package from the database
		// 	cmd.EditPackage,   // editing a package from the database
		// 	cmd.RemovePackage, // removing a package from the database
	}
	app.Run(os.Args)

}
<commit_msg>changed doc<commit_after>// Copyright 2014 Jesper Brodersen. All rights reserved.
// This code is BSD-licensed, see LICENSE file.

// pm-tools is a package manger written in Go Language
// this is the admin application for running CRUD-operations on packages,
// and settings management.
package main

import (
	"github.com/broeman/pm-tools/cmd" // using CLI command args
	"github.com/codegangsta/cli"
	"os"
	"runtime"
)

const MAN_VER = "0.1 Alpha"

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())
}

func main() {
	app := cli.NewApp()
	app.Name = "pm-tools"
	app.Usage = "Package Manager in Go: Toolkit"
	app.Version = MAN_VER
	app.Commands = []cli.Command{
		cmd.Init,      // initialize database if not exist, placeholder for setup
		cmd.Installed, // shows the installed packages, placeholder for testing
		//  cmd.Setup,	// settings for the user, init db
		// 	cmd.AddPackage,    // adding a package to the database
		// 	cmd.ShowPackage,   // showing a package from the database
		// 	cmd.EditPackage,   // editing a package from the database
		// 	cmd.RemovePackage, // removing a package from the database
	}
	app.Run(os.Args)

}
<|endoftext|>


<commit_before>package scopenv

import (
	"fmt"
	"os"
	"strings"
)

// Scopenv - REDEFINE CREATE : Maybe also return error
func Scopenv(m []string, v ...string) map[string]string {

	r, _ := reverse(v)
	s := make(map[string]string)

	scope := find(s, m, r)

	return scope
}

func find(s map[string]string, m, rev []string) map[string]string {
	for i := range m {
		tmp := strings.Join(rev, "_")
		f := upper(tmp) + "_" + upper(m[i])
		if len(rev) == 0 {
			f = strings.Replace(f, "_", "", 1)
		}
		check := os.Getenv(f)
		if check == "" {
			// Check for the length of the slice before removing
			if len(rev) == 0 {
				panic(fmt.Sprintf("Error: you are missing a env variable for one of the values in m."))
			}
			rev = removeFirst(rev)

			find(s, m, rev)
		} else {
			s[m[i]] = check
		}
	}

	return s
}

// reverse - reverses the order of a slice of strings
func reverse(v []string) ([]string, int) {
	var r []string
	for i := range v {
		l := v[len(v)-1-i]
		r = append(r, l) // Suggestion: do `last := len(s)-1` before the loop

	}
	leng := len(r)
	return r, leng
}

// upper - Helper function for capitalizing arguments
func upper(arg string) string {
	return strings.ToUpper(arg)
}

// removeFirst - removes the first item in a slice
func removeFirst(s []string) []string {
	s = append(s[:0], s[1:]...)
	return s
}
<commit_msg>Adding comments<commit_after>package scopenv

import (
	"fmt"
	"os"
	"strings"
)

// Scopenv - Main function of this package
func Scopenv(m []string, v ...string) map[string]string {

	r, _ := reverse(v)
	s := make(map[string]string)

	scope := find(s, m, r)

	return scope
}

// find - recurively finds and sets up env variables
func find(s map[string]string, m, rev []string) map[string]string {
	for i := range m {
		// Combine prefixes with variables and uppercase them
		tmp := strings.Join(rev, "_")
		f := upper(tmp) + "_" + upper(m[i])

		// If there are no more prefixes, remove the leading _
		if len(rev) == 0 {
			f = strings.Replace(f, "_", "", 1)
		}
		// Get your env vars
		check := os.Getenv(f)
		if check == "" {
			// Check for the length of the slice before removing
			if len(rev) == 0 {
				panic(fmt.Sprintf("Error: you are missing a env variable for one of the values in m."))
			}
			rev = removeFirst(rev)

			// Recursion !!!
			find(s, m, rev)
		} else {
			// Put the env vars in the map
			s[m[i]] = check
		}
	}

	return s
}

// reverse - reverses the order of a slice of strings
func reverse(v []string) ([]string, int) {
	var r []string
	for i := range v {
		l := v[len(v)-1-i]
		r = append(r, l)

	}
	leng := len(r)
	return r, leng
}

// upper - Helper function for capitalizing arguments
func upper(arg string) string {
	return strings.ToUpper(arg)
}

// removeFirst - removes the first item in a slice
func removeFirst(s []string) []string {
	s = append(s[:0], s[1:]...)
	return s
}
<|endoftext|>


<commit_before>package sharings

import (
	"encoding/json"
	"io"
	"net/http"
	"time"

	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/vfs"
	"github.com/cozy/cozy-stack/web/files"
	"github.com/cozy/cozy-stack/web/jsonapi"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/cozy/cozy-stack/web/permissions"
	"github.com/labstack/echo"
)

// TODO Support sharing of recursive directories. For now all directories go
// to /Shared With Me/
func creationWithIDHandler(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	fs := instance.VFS()

	_, err := fs.DirByID(consts.SharedWithMeDirID)
	if err != nil {
		err = createSharedWithMeDir(fs)
		if err != nil {
			return err
		}
	}

	switch c.QueryParam("Type") {
	case consts.FileType:
		err = createFileWithIDHandler(c, fs)
	case consts.DirType:
		err = createDirWithIDHandler(c, fs)
	default:
		return files.ErrDocTypeInvalid
	}

	return err
}

func createDirWithIDHandler(c echo.Context, fs vfs.VFS) error {
	name := c.QueryParam("Name")
	id := c.Param("docid")
	date := c.Request().Header.Get("Date")

	// TODO handle name collision.
	doc, err := vfs.NewDirDoc(fs, name, "", nil)
	if err != nil {
		return err
	}

	doc.DirID = consts.SharedWithMeDirID
	doc.SetID(id)

	if date != "" {
		if t, errt := time.Parse(time.RFC1123, date); errt == nil {
			doc.CreatedAt = t
			doc.UpdatedAt = t
		}
	}

	if err = permissions.AllowVFS(c, "POST", doc); err != nil {
		return err
	}

	return fs.CreateDir(doc)
}

func createFileWithIDHandler(c echo.Context, fs vfs.VFS) error {
	name := c.QueryParam("Name")

	doc, err := files.FileDocFromReq(c, name, "", nil)
	if err != nil {
		return err
	}

	doc.SetID(c.Param("docid"))
	doc.DirID = consts.SharedWithMeDirID

	refBy := c.QueryParam("Referenced_by")
	if refBy != "" {
		var refs = []couchdb.DocReference{}
		b := []byte(refBy)
		if err = json.Unmarshal(b, &refs); err != nil {
			return err
		}
		doc.ReferencedBy = refs
	}

	if err = permissions.AllowVFS(c, "POST", doc); err != nil {
		return err
	}

	file, err := fs.CreateFile(doc, nil)
	if err != nil {
		return err
	}

	defer func() {
		if cerr := file.Close(); cerr != nil && err == nil {
			err = cerr
		}
	}()

	_, err = io.Copy(file, c.Request().Body)
	return err
}

func createSharedWithMeDir(fs vfs.VFS) error {
	// TODO Put "Shared With Me" in a local-aware constant.
	dirDoc, err := vfs.NewDirDoc(fs, "Shared With Me", "", nil)
	if err != nil {
		return err
	}

	dirDoc.SetID(consts.SharedWithMeDirID)
	t := time.Now()
	dirDoc.CreatedAt = t
	dirDoc.UpdatedAt = t

	return fs.CreateDir(dirDoc)
}

func updateFile(c echo.Context) error {
	fs := middlewares.GetInstance(c).VFS()
	olddoc, err := fs.FileByID(c.Param("docid"))
	if err != nil {
		return err
	}

	newdoc, err := files.FileDocFromReq(
		c,
		c.QueryParam("Name"),
		// TODO Handle dir hierarchy within a sharing and stop putting
		// everything in "Shared With Me".
		consts.SharedWithMeDirID,
		olddoc.Tags,
	)
	newdoc.ReferencedBy = olddoc.ReferencedBy

	if err = files.CheckIfMatch(c, olddoc.Rev()); err != nil {
		return err
	}

	if err = permissions.AllowVFS(c, permissions.PUT, olddoc); err != nil {
		return err
	}

	// The permission is on the ID so the newdoc has to have the same ID or
	// the permission check will fail.
	newdoc.SetID(olddoc.ID())
	if err = permissions.AllowVFS(c, permissions.PUT, newdoc); err != nil {
		return err
	}

	file, err := fs.CreateFile(newdoc, olddoc)
	if err != nil {
		return err
	}

	defer func() {
		if cerr := file.Close(); cerr != nil && err == nil {
			err = cerr
		}
		if err != nil {
			return
		}
		err = c.JSON(http.StatusOK, nil)
	}()

	_, err = io.Copy(file, c.Request().Body)
	return err
}

func patchDirOrFile(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	var patch vfs.DocPatch

	_, err := jsonapi.Bind(c.Request(), &patch)
	if err != nil {
		return jsonapi.BadJSON()
	}

	// TODO When supported re-apply hierarchy here.

	*patch.DirID = consts.SharedWithMeDirID
	patch.RestorePath = nil

	dirDoc, fileDoc, err := instance.VFS().DirOrFileByID(c.Param("docid"))
	if err != nil {
		return err
	}

	var rev string
	if dirDoc != nil {
		rev = dirDoc.Rev()
	} else {
		rev = fileDoc.Rev()
	}

	if errc := files.CheckIfMatch(c, rev); err != nil {
		return errc
	}

	if dirDoc != nil {
		_, err = vfs.ModifyDirMetadata(instance.VFS(), dirDoc, &patch)
		if err != nil {
			return err
		}
		return c.JSON(http.StatusOK, nil)
	}

	_, err = vfs.ModifyFileMetadata(instance.VFS(), fileDoc, &patch)
	if err != nil {
		return err
	}
	return c.JSON(http.StatusOK, nil)
}

func trashHandler(c echo.Context) error {
	instance := middlewares.GetInstance(c)

	fileID := c.Param("docid")

	dir, file, err := instance.VFS().DirOrFileByID(fileID)
	if err != nil {
		return err
	}

	var rev string
	if dir != nil {
		rev = dir.Rev()
	} else {
		rev = file.Rev()
	}

	if err := files.CheckIfMatch(c, rev); err != nil {
		return err
	}

	if dir != nil {
		_, errt := vfs.TrashDir(instance.VFS(), dir)
		if errt != nil {
			return err
		}
		return nil
	}

	_, errt := vfs.TrashFile(instance.VFS(), file)
	if errt != nil {
		return err
	}
	return nil
}
<commit_msg>fix: gometalinter<commit_after>package sharings

import (
	"encoding/json"
	"io"
	"net/http"
	"time"

	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/vfs"
	"github.com/cozy/cozy-stack/web/files"
	"github.com/cozy/cozy-stack/web/jsonapi"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/cozy/cozy-stack/web/permissions"
	"github.com/labstack/echo"
)

// TODO Support sharing of recursive directories. For now all directories go
// to /Shared With Me/
func creationWithIDHandler(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	fs := instance.VFS()

	_, err := fs.DirByID(consts.SharedWithMeDirID)
	if err != nil {
		err = createSharedWithMeDir(fs)
		if err != nil {
			return err
		}
	}

	switch c.QueryParam("Type") {
	case consts.FileType:
		err = createFileWithIDHandler(c, fs)
	case consts.DirType:
		err = createDirWithIDHandler(c, fs)
	default:
		return files.ErrDocTypeInvalid
	}

	return err
}

func createDirWithIDHandler(c echo.Context, fs vfs.VFS) error {
	name := c.QueryParam("Name")
	id := c.Param("docid")
	date := c.Request().Header.Get("Date")

	// TODO handle name collision.
	doc, err := vfs.NewDirDoc(fs, name, "", nil)
	if err != nil {
		return err
	}

	doc.DirID = consts.SharedWithMeDirID
	doc.SetID(id)

	if date != "" {
		if t, errt := time.Parse(time.RFC1123, date); errt == nil {
			doc.CreatedAt = t
			doc.UpdatedAt = t
		}
	}

	if err = permissions.AllowVFS(c, "POST", doc); err != nil {
		return err
	}

	return fs.CreateDir(doc)
}

func createFileWithIDHandler(c echo.Context, fs vfs.VFS) error {
	name := c.QueryParam("Name")

	doc, err := files.FileDocFromReq(c, name, "", nil)
	if err != nil {
		return err
	}

	doc.SetID(c.Param("docid"))
	doc.DirID = consts.SharedWithMeDirID

	refBy := c.QueryParam("Referenced_by")
	if refBy != "" {
		var refs = []couchdb.DocReference{}
		b := []byte(refBy)
		if err = json.Unmarshal(b, &refs); err != nil {
			return err
		}
		doc.ReferencedBy = refs
	}

	if err = permissions.AllowVFS(c, "POST", doc); err != nil {
		return err
	}

	file, err := fs.CreateFile(doc, nil)
	if err != nil {
		return err
	}

	defer func() {
		if cerr := file.Close(); cerr != nil && err == nil {
			err = cerr
		}
	}()

	_, err = io.Copy(file, c.Request().Body)
	return err
}

func createSharedWithMeDir(fs vfs.VFS) error {
	// TODO Put "Shared With Me" in a local-aware constant.
	dirDoc, err := vfs.NewDirDoc(fs, "Shared With Me", "", nil)
	if err != nil {
		return err
	}

	dirDoc.SetID(consts.SharedWithMeDirID)
	t := time.Now()
	dirDoc.CreatedAt = t
	dirDoc.UpdatedAt = t

	return fs.CreateDir(dirDoc)
}

func updateFile(c echo.Context) error {
	fs := middlewares.GetInstance(c).VFS()
	olddoc, err := fs.FileByID(c.Param("docid"))
	if err != nil {
		return err
	}

	newdoc, err := files.FileDocFromReq(
		c,
		c.QueryParam("Name"),
		// TODO Handle dir hierarchy within a sharing and stop putting
		// everything in "Shared With Me".
		consts.SharedWithMeDirID,
		olddoc.Tags,
	)
	newdoc.ReferencedBy = olddoc.ReferencedBy

	if err = files.CheckIfMatch(c, olddoc.Rev()); err != nil {
		return err
	}

	if err = permissions.AllowVFS(c, permissions.PUT, olddoc); err != nil {
		return err
	}

	// The permission is on the ID so the newdoc has to have the same ID or
	// the permission check will fail.
	newdoc.SetID(olddoc.ID())
	if err = permissions.AllowVFS(c, permissions.PUT, newdoc); err != nil {
		return err
	}

	file, err := fs.CreateFile(newdoc, olddoc)
	if err != nil {
		return err
	}

	defer func() {
		if cerr := file.Close(); cerr != nil && err == nil {
			err = cerr
		}
		if err != nil {
			return
		}
		err = c.JSON(http.StatusOK, nil)
	}()

	_, err = io.Copy(file, c.Request().Body)
	return err
}

func patchDirOrFile(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	var patch vfs.DocPatch

	_, err := jsonapi.Bind(c.Request(), &patch)
	if err != nil {
		return jsonapi.BadJSON()
	}

	// TODO When supported re-apply hierarchy here.

	*patch.DirID = consts.SharedWithMeDirID
	patch.RestorePath = nil

	dirDoc, fileDoc, err := instance.VFS().DirOrFileByID(c.Param("docid"))
	if err != nil {
		return err
	}

	var rev string
	if dirDoc != nil {
		rev = dirDoc.Rev()
	} else {
		rev = fileDoc.Rev()
	}

	if errc := files.CheckIfMatch(c, rev); err != nil {
		return errc
	}

	if dirDoc != nil {
		_, err = vfs.ModifyDirMetadata(instance.VFS(), dirDoc, &patch)
		if err != nil {
			return err
		}
		return c.JSON(http.StatusOK, nil)
	}

	_, err = vfs.ModifyFileMetadata(instance.VFS(), fileDoc, &patch)
	if err != nil {
		return err
	}
	return c.JSON(http.StatusOK, nil)
}

func trashHandler(c echo.Context) error {
	instance := middlewares.GetInstance(c)

	fileID := c.Param("docid")

	dir, file, err := instance.VFS().DirOrFileByID(fileID)
	if err != nil {
		return err
	}

	var rev string
	if dir != nil {
		rev = dir.Rev()
	} else {
		rev = file.Rev()
	}

	if err = files.CheckIfMatch(c, rev); err != nil {
		return err
	}

	if dir != nil {
		_, errt := vfs.TrashDir(instance.VFS(), dir)
		if errt != nil {
			return err
		}
		return nil
	}

	_, errt := vfs.TrashFile(instance.VFS(), file)
	if errt != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package sharings

import (
	"encoding/json"
	"io"
	"time"

	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/vfs"
	"github.com/cozy/cozy-stack/web/files"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/cozy/cozy-stack/web/permissions"
	"github.com/labstack/echo"
)

// TODO Support sharing of recursive directories. For now all directories go
// to /Shared With Me/
func creationWithIDHandler(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	fs := instance.VFS()

	_, err := fs.DirByID(consts.SharedWithMeDirID)
	if err != nil {
		err = createSharedWithMeDir(fs)
		if err != nil {
			return err
		}
	}

	switch c.QueryParam("Type") {
	case consts.FileType:
		err = createFileWithIDHandler(c, fs)
	case consts.DirType:
		err = createDirWithIDHandler(c, fs)
	default:
		return files.ErrDocTypeInvalid
	}

	return err
}

func createDirWithIDHandler(c echo.Context, fs vfs.VFS) error {
	name := c.QueryParam("Name")
	id := c.Param("docid")
	date := c.Request().Header.Get("Date")

	// TODO handle name collision.
	doc, err := vfs.NewDirDoc(fs, name, "", nil)
	if err != nil {
		return err
	}

	doc.DirID = consts.SharedWithMeDirID
	doc.SetID(id)

	if date != "" {
		if t, errt := time.Parse(time.RFC1123, date); errt == nil {
			doc.CreatedAt = t
			doc.UpdatedAt = t
		}
	}

	if err = permissions.AllowVFS(c, "POST", doc); err != nil {
		return err
	}

	return fs.CreateDir(doc)
}

func createFileWithIDHandler(c echo.Context, fs vfs.VFS) error {
	name := c.QueryParam("Name")

	doc, err := files.FileDocFromReq(c, name, "", nil)
	if err != nil {
		return err
	}

	doc.SetID(c.Param("docid"))
	doc.DirID = consts.SharedWithMeDirID

	refBy := c.QueryParam("Referenced_by")
	if refBy != "" {
		var refs = &[]couchdb.DocReference{}
		b := []byte(refBy)
		if err = json.Unmarshal(b, refs); err != nil {
			return err
		}
		doc.ReferencedBy = *refs
	}

	if err = permissions.AllowVFS(c, "POST", doc); err != nil {
		return err
	}

	file, err := fs.CreateFile(doc, nil)
	if err != nil {
		return err
	}

	defer func() {
		if cerr := file.Close(); cerr != nil && err == nil {
			err = cerr
		}
	}()

	_, err = io.Copy(file, c.Request().Body)
	return err
}

func createSharedWithMeDir(fs vfs.VFS) error {
	// TODO Put "Shared With Me" in a local-aware constant.
	dirDoc, err := vfs.NewDirDoc(fs, "Shared With Me", "", nil)
	if err != nil {
		return err
	}

	dirDoc.SetID(consts.SharedWithMeDirID)
	t := time.Now()
	dirDoc.CreatedAt = t
	dirDoc.UpdatedAt = t

	return fs.CreateDir(dirDoc)
}
<commit_msg>Clearer pointer use<commit_after>package sharings

import (
	"encoding/json"
	"io"
	"time"

	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/vfs"
	"github.com/cozy/cozy-stack/web/files"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/cozy/cozy-stack/web/permissions"
	"github.com/labstack/echo"
)

// TODO Support sharing of recursive directories. For now all directories go
// to /Shared With Me/
func creationWithIDHandler(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	fs := instance.VFS()

	_, err := fs.DirByID(consts.SharedWithMeDirID)
	if err != nil {
		err = createSharedWithMeDir(fs)
		if err != nil {
			return err
		}
	}

	switch c.QueryParam("Type") {
	case consts.FileType:
		err = createFileWithIDHandler(c, fs)
	case consts.DirType:
		err = createDirWithIDHandler(c, fs)
	default:
		return files.ErrDocTypeInvalid
	}

	return err
}

func createDirWithIDHandler(c echo.Context, fs vfs.VFS) error {
	name := c.QueryParam("Name")
	id := c.Param("docid")
	date := c.Request().Header.Get("Date")

	// TODO handle name collision.
	doc, err := vfs.NewDirDoc(fs, name, "", nil)
	if err != nil {
		return err
	}

	doc.DirID = consts.SharedWithMeDirID
	doc.SetID(id)

	if date != "" {
		if t, errt := time.Parse(time.RFC1123, date); errt == nil {
			doc.CreatedAt = t
			doc.UpdatedAt = t
		}
	}

	if err = permissions.AllowVFS(c, "POST", doc); err != nil {
		return err
	}

	return fs.CreateDir(doc)
}

func createFileWithIDHandler(c echo.Context, fs vfs.VFS) error {
	name := c.QueryParam("Name")

	doc, err := files.FileDocFromReq(c, name, "", nil)
	if err != nil {
		return err
	}

	doc.SetID(c.Param("docid"))
	doc.DirID = consts.SharedWithMeDirID

	refBy := c.QueryParam("Referenced_by")
	if refBy != "" {
		var refs = []couchdb.DocReference{}
		b := []byte(refBy)
		if err = json.Unmarshal(b, &refs); err != nil {
			return err
		}
		doc.ReferencedBy = refs
	}

	if err = permissions.AllowVFS(c, "POST", doc); err != nil {
		return err
	}

	file, err := fs.CreateFile(doc, nil)
	if err != nil {
		return err
	}

	defer func() {
		if cerr := file.Close(); cerr != nil && err == nil {
			err = cerr
		}
	}()

	_, err = io.Copy(file, c.Request().Body)
	return err
}

func createSharedWithMeDir(fs vfs.VFS) error {
	// TODO Put "Shared With Me" in a local-aware constant.
	dirDoc, err := vfs.NewDirDoc(fs, "Shared With Me", "", nil)
	if err != nil {
		return err
	}

	dirDoc.SetID(consts.SharedWithMeDirID)
	t := time.Now()
	dirDoc.CreatedAt = t
	dirDoc.UpdatedAt = t

	return fs.CreateDir(dirDoc)
}
<|endoftext|>


<commit_before>package functions

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"strconv"
	"time"
)

// A cumulative probability list for each categroy
var categoryProbabilities = map[string]float64{
	"area":              0.2,
	"population":        0.4,
	"gdpPerCapita":      0.6,
	"lifeExpectancy":    0.8,
	"healthExpenditure": 0.9,
	"gini":              1.0,
}

var excludedCountries = []string{
	"um", // United States Pacific Island Wildlife Refuges
	"wf", // Wallis and Futuna
	"bq", // Navassa Island
	"at", // Ashmore and Cartier Islands
	"kt", // Christmas Island
	"ck", // Cocos (Keeling) Islands
	"cr", // Coral Sea Islands
	"ne", // Niue
	"nf", // Norfolk Island
	"cq", // Northern Mariana Islands
	"tl", // Tokelau
	"tb", // Saint Barthelemy
	"dx", // Dhekelia
	"jn", // Jan Mayen
	"je", // Jersey
	"ip", // Clipperton Island
	"sb", // Saint Pierre and Miquelon
	"io", // British Indian Ocean Territory
	"sh", // Saint Helena, Ascension, and Tristan da Cunha
	"bv", // Bouvet Island
	"fs", // French Southern and Antarctic Lands
	"hm", // Heard Island and McDonald Islands
	"nc", // New Caledonia
	"wq", // Wake Island
	"nr", // Nauru
	"vc", // Saint Vincent and the Grenadines
	"pf", // Paracel Islands
	"kr", // Kiribati
	"cc", // Curacao
	"tn", // Tonga
}

// DataHTTP is an HTTP Cloud Function.
func Questions(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query()
	n, present := query["n"]

	if !present {
		n = []string{"30"}
	}
	numQuestions, _ := strconv.Atoi(n[0])

	countries, readError := Read()
	if readError != nil {
		http.Error(w, "500 - Could not read file", http.StatusInternalServerError)
		return
	}

	// fmt.Fprintf(w, "Hello, %s!", html.EscapeString(fmt.Sprintf("hello hello %d", numQuestions)))

	questions := GetQuestions(countries, numQuestions)
	json, err := json.Marshal(questions)
	if err != nil {
		http.Error(w, "500 - Could not read file", http.StatusInternalServerError)
		return
	}

	// Set CORS headers for the preflight request
	if r.Method == http.MethodOptions {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "POST")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
		w.Header().Set("Access-Control-Max-Age", "3600")
		w.WriteHeader(http.StatusNoContent)
		return
	}
	// Set CORS headers for the main request.
	w.Header().Set("Access-Control-Allow-Origin", "*")

	fmt.Fprint(w, string(json))
}

func Read() (map[string]Country, error) {
	byteValue, err := ioutil.ReadFile("./serverless_function_source_code/factbook.json")
	if err != nil {
		// fallback to non-cloud folder
		// see https://cloud.google.com/functions/docs/concepts/exec#file_system
		byteValue, err = ioutil.ReadFile("./factbook.json")
		if err != nil {
			return nil, err
		}
	}

	decoder := json.NewDecoder(bytes.NewReader(byteValue))
	var countries map[string]Country
	err = decoder.Decode(&countries)

	if err != nil {
		return nil, err
	}

	return countries, nil
}

func GetQuestions(countries map[string]Country, n int) []Question {
	var questions []Question
	i := 0
	for i < n {
		c1, c2 := getTwoCountries(countries)
		var q Question
		var v1, v2 float32
		q.Category = getRandomCategory()
		switch q.Category {
		case "area":
			v1, v2 = c1.Area.Value, c2.Area.Value
			q.Text = fmt.Sprintf("%s is bigger than %s.", c1.Name, c2.Name)
			q.Hint = fmt.Sprintf("%s: %s
%s: %s", c1.Name, c1.Area.Text, c2.Name, c2.Area.Text)
		case "population":
			v1, v2 = c1.Population.Value, c2.Population.Value
			q.Text = fmt.Sprintf("%s has more people than %s.", c1.Name, c2.Name)
			q.Hint = fmt.Sprintf("%s: population growth rate of %.2f
%s: population growth rate of %.2f", c1.Name, v1, c2.Name, v2)
		case "gdpPerCapita":
			v1, v2 = c1.GDPCapita.Value, c2.GDPCapita.Value
			q.Text = fmt.Sprintf("%s has higher GDP (PPP) per capita than %s.", c1.Name, c2.Name)
			cOneYear, cTwoYear := "", ""
			if c1.GDPCapita.Text != "" {
				cOneYear = " (" + c1.GDPCapita.Text + ")"
			}
			if c2.GDPCapita.Text != "" {
				cTwoYear = " (" + c2.GDPCapita.Text + ")"
			}
			q.Hint = fmt.Sprintf("%s: total GDP is %.0f billion%s
%s: total GDP is %.0f billion%s", c1.Name, v1, cOneYear, c2.Name, v2, cTwoYear)
		case "healthExpenditure":
			v1, v2 = c1.HealthExpenditure.Value, c2.HealthExpenditure.Value
			q.Text = fmt.Sprintf("%s has higher health expenditure (%%GDP) than %s.", c1.Name, c2.Name)
			q.Hint = fmt.Sprintf("%s: death rate of %.2f
%s: death rate of %.2f", c1.Name, c1.DeathRate.Value, c2.Name, c2.DeathRate.Value)
		case "gini":
			v1, v2 = c1.Gini.Value, c2.Gini.Value
			q.Text = fmt.Sprintf("%s has a higher Gini index than %s.", c1.Name, c2.Name)
			q.Hint = "The Gini index is a measure of income
inequality. Higher values mean higher
inequality."
		case "lifeExpectancy":
			v1, v2 = c1.LifeExpectancy.Value, c2.LifeExpectancy.Value
			q.Text = fmt.Sprintf("%s has a higher life expectancy at birth than %s.", c1.Name, c2.Name)
			q.Hint = fmt.Sprintf("%s: fertility rate of %.2f
%s: fertility rate of %.2f", c1.Name, c1.TotalFertilityRate.Value, c2.Name, c2.TotalFertilityRate.Value)
		}

		if v1 != 0 && v2 != 0 {
			q.Feedback = Feedback{Category: q.Category, Values: []NamedValue{{c1.Name, v1}, {c2.Name, v2}}}
			q.Fact = strconv.FormatBool(v1 > v2)
			q.Options = []string{"true", "false"}
			questions = append(questions, q)
			i++
		}

	}
	return questions
}

func getRandomCategory() string {
	r := rand.Float64()
	for k, p := range categoryProbabilities {
		if r <= p {
			return k
		}
	}
	return ""
}

func getTwoCountries(countries map[string]Country) (Country, Country) {
	rand.Seed(time.Now().UnixNano())
	keys := GetKeysCountry(countries)

	countryOne := countries[GetRandom(keys)]
	countryTwo := countries[GetRandom(keys)]

	if contains(excludedCountries, countryOne.Id) || contains(excludedCountries, countryTwo.Id) || countryOne == countryTwo {
		return getTwoCountries(countries)
	} else {
		return countryOne, countryTwo
	}
}
<commit_msg>Cleanup.<commit_after>package functions

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"strconv"
	"time"
)

// A cumulative probability list for each categroy
var categoryProbabilities = map[string]float64{
	"area":              0.2,
	"population":        0.4,
	"gdpPerCapita":      0.6,
	"lifeExpectancy":    0.8,
	"healthExpenditure": 0.9,
	"gini":              1.0,
}

var excludedCountries = []string{
	"um", // United States Pacific Island Wildlife Refuges
	"wf", // Wallis and Futuna
	"bq", // Navassa Island
	"at", // Ashmore and Cartier Islands
	"kt", // Christmas Island
	"ck", // Cocos (Keeling) Islands
	"cr", // Coral Sea Islands
	"ne", // Niue
	"nf", // Norfolk Island
	"cq", // Northern Mariana Islands
	"tl", // Tokelau
	"tb", // Saint Barthelemy
	"dx", // Dhekelia
	"jn", // Jan Mayen
	"je", // Jersey
	"ip", // Clipperton Island
	"sb", // Saint Pierre and Miquelon
	"io", // British Indian Ocean Territory
	"sh", // Saint Helena, Ascension, and Tristan da Cunha
	"bv", // Bouvet Island
	"fs", // French Southern and Antarctic Lands
	"hm", // Heard Island and McDonald Islands
	"nc", // New Caledonia
	"wq", // Wake Island
	"nr", // Nauru
	"vc", // Saint Vincent and the Grenadines
	"pf", // Paracel Islands
	"kr", // Kiribati
	"cc", // Curacao
	"tn", // Tonga
}

// DataHTTP is an HTTP Cloud Function.
func Questions(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query()
	n, present := query["n"]

	if !present {
		n = []string{"30"}
	}
	numQuestions, _ := strconv.Atoi(n[0])

	countries, readError := Read()
	if readError != nil {
		http.Error(w, "500 - Could not read file", http.StatusInternalServerError)
		return
	}

	// fmt.Fprintf(w, "Hello, %s!", html.EscapeString(fmt.Sprintf("hello hello %d", numQuestions)))

	questions := GetQuestions(countries, numQuestions)
	json, err := json.Marshal(questions)
	if err != nil {
		http.Error(w, "500 - Could not read file", http.StatusInternalServerError)
		return
	}

	// Set CORS headers for the preflight request
	if r.Method == http.MethodOptions {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "POST")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
		w.Header().Set("Access-Control-Max-Age", "3600")
		w.WriteHeader(http.StatusNoContent)
		return
	}
	// Set CORS headers for the main request.
	w.Header().Set("Access-Control-Allow-Origin", "*")

	fmt.Fprint(w, string(json))
}

func Read() (map[string]Country, error) {
	byteValue, err := ioutil.ReadFile("./serverless_function_source_code/factbook.json")
	if err != nil {
		// fallback to non-cloud folder
		// see https://cloud.google.com/functions/docs/concepts/exec#file_system
		byteValue, err = ioutil.ReadFile("./factbook.json")
		if err != nil {
			return nil, err
		}
	}

	decoder := json.NewDecoder(bytes.NewReader(byteValue))
	var countries map[string]Country
	err = decoder.Decode(&countries)

	if err != nil {
		return nil, err
	}

	return countries, nil
}

func GetQuestions(countries map[string]Country, n int) []Question {
	var questions []Question
	i := 0
	for i < n {
		c1, c2 := getTwoCountries(countries)
		var q Question
		var v1, v2 float32
		q.Category = getRandomCategory()
		switch q.Category {
		case "area":
			v1, v2 = c1.Area.Value, c2.Area.Value
			q.Text = fmt.Sprintf("%s is bigger than %s.", c1.Name, c2.Name)
			q.Hint = fmt.Sprintf("%s: %s
%s: %s", c1.Name, c1.Area.Text, c2.Name, c2.Area.Text)
		case "population":
			v1, v2 = c1.Population.Value, c2.Population.Value
			q.Text = fmt.Sprintf("%s has more people than %s.", c1.Name, c2.Name)
			q.Hint = fmt.Sprintf("%s: population growth rate of %.2f
%s: population growth rate of %.2f", c1.Name, v1, c2.Name, v2)
		case "gdpPerCapita":
			v1, v2 = c1.GDPCapita.Value, c2.GDPCapita.Value
			q.Text = fmt.Sprintf("%s has higher GDP (PPP) per capita than %s.", c1.Name, c2.Name)
			cOneYear, cTwoYear := "", ""
			if c1.GDPCapita.Text != "" {
				cOneYear = " (" + c1.GDPCapita.Text + ")"
			}
			if c2.GDPCapita.Text != "" {
				cTwoYear = " (" + c2.GDPCapita.Text + ")"
			}
			q.Hint = fmt.Sprintf("%s: total GDP is %.0f billion%s
%s: total GDP is %.0f billion%s", c1.Name, v1, cOneYear, c2.Name, v2, cTwoYear)
		case "healthExpenditure":
			v1, v2 = c1.HealthExpenditure.Value, c2.HealthExpenditure.Value
			q.Text = fmt.Sprintf("%s has higher health expenditure (%%GDP) than %s.", c1.Name, c2.Name)
			q.Hint = fmt.Sprintf("%s: death rate of %.2f
%s: death rate of %.2f", c1.Name, c1.DeathRate.Value, c2.Name, c2.DeathRate.Value)
		case "gini":
			v1, v2 = c1.Gini.Value, c2.Gini.Value
			q.Text = fmt.Sprintf("%s has a higher Gini index than %s.", c1.Name, c2.Name)
			q.Hint = "The Gini index is a measure of income
inequality. Higher values mean higher
inequality."
		case "lifeExpectancy":
			v1, v2 = c1.LifeExpectancy.Value, c2.LifeExpectancy.Value
			q.Text = fmt.Sprintf("%s has a higher life expectancy at birth than %s.", c1.Name, c2.Name)
			q.Hint = fmt.Sprintf("%s: fertility rate of %.2f
%s: fertility rate of %.2f", c1.Name, c1.TotalFertilityRate.Value, c2.Name, c2.TotalFertilityRate.Value)
		}

		if v1 != 0 && v2 != 0 {
			q.Feedback = Feedback{Category: q.Category, Values: []NamedValue{{c1.Name, v1}, {c2.Name, v2}}}
			q.Fact = strconv.FormatBool(v1 > v2)
			q.Options = []string{"true", "false"}
			questions = append(questions, q)
			i++
		}

	}
	return questions
}

func getRandomCategory() string {
	r := rand.Float64()
	for k, p := range categoryProbabilities {
		if r <= p {
			return k
		}
	}
	return ""
}

func getTwoCountries(countries map[string]Country) (Country, Country) {
	rand.Seed(time.Now().UnixNano())
	keys := GetKeysCountry(countries)

	countryOne := countries[GetRandom(keys)]
	countryTwo := countries[GetRandom(keys)]

	if contains(excludedCountries, countryOne.Id) || contains(excludedCountries, countryTwo.Id) || countryOne == countryTwo {
		return getTwoCountries(countries)
	}
	return countryOne, countryTwo
}
<|endoftext|>