<commit_before>package qemu

import (
	"archive/tar"
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/mcnflag"
	"github.com/docker/machine/libmachine/mcnutils"
	"github.com/docker/machine/libmachine/ssh"
	"github.com/docker/machine/libmachine/state"
)

const (
	isoFilename        = "boot2docker.iso"
	privateNetworkName = "docker-machines"

	defaultSSHUser = "docker"
)

type Driver struct {
	*drivers.BaseDriver

	Memory           int
	DiskSize         int
	CPU              int
	Network          string
	PrivateNetwork   string
	ISO              string
	Boot2DockerURL   string
	CaCertPath       string
	PrivateKeyPath   string
	DiskPath         string
	CacheMode        string
	IOMode           string
	connectionString string
	//	conn             *libvirt.Connect
	//	VM               *libvirt.Domain
	vmLoaded bool
}

func (d *Driver) GetCreateFlags() []mcnflag.Flag {
	return []mcnflag.Flag{
		mcnflag.IntFlag{
			Name:  "qemu-memory",
			Usage: "Size of memory for host in MB",
			Value: 1024,
		},
		mcnflag.IntFlag{
			Name:  "qemu-disk-size",
			Usage: "Size of disk for host in MB",
			Value: 20000,
		},
		mcnflag.IntFlag{
			Name:  "qemu-cpu-count",
			Usage: "Number of CPUs",
			Value: 1,
		},
		// TODO - support for multiple networks
		mcnflag.StringFlag{
			Name:  "qemu-network",
			Usage: "Name of network to connect to",
			Value: "default",
		},
		mcnflag.StringFlag{
			EnvVar: "KVM_BOOT2DOCKER_URL",
			Name:   "qemu-boot2docker-url",
			Usage:  "The URL of the boot2docker image. Defaults to the latest available version",
			Value:  "",
		},
		mcnflag.StringFlag{
			Name:  "qemu-cache-mode",
			Usage: "Disk cache mode: default, none, writethrough, writeback, directsync, or unsafe",
			Value: "default",
		},
		mcnflag.StringFlag{
			Name:  "qemu-io-mode",
			Usage: "Disk IO mode: threads, native",
			Value: "threads",
		},
		mcnflag.StringFlag{
			EnvVar: "KVM_SSH_USER",
			Name:   "qemu-ssh-user",
			Usage:  "SSH username",
			Value:  defaultSSHUser,
		},
		/* Not yet implemented
		mcnflag.Flag{
			Name:  "qemu-no-share",
			Usage: "Disable the mount of your home directory",
		},
		*/
	}
}

func (d *Driver) GetMachineName() string {
	return d.MachineName
}

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

func (d *Driver) GetSSHKeyPath() string {
	return d.ResolveStorePath("id_rsa")
}

func (d *Driver) GetSSHPort() (int, error) {
	if d.SSHPort == 0 {
		d.SSHPort = 22
	}

	return d.SSHPort, nil
}

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

	return d.SSHUser
}

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

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
	log.Debugf("SetConfigFromFlags called")
	d.Memory = flags.Int("qemu-memory")
	d.DiskSize = flags.Int("qemu-disk-size")
	d.CPU = flags.Int("qemu-cpu-count")
	d.Network = flags.String("qemu-network")
	d.Boot2DockerURL = flags.String("qemu-boot2docker-url")
	d.CacheMode = flags.String("qemu-cache-mode")
	d.IOMode = flags.String("qemu-io-mode")

	d.SwarmMaster = flags.Bool("swarm-master")
	d.SwarmHost = flags.String("swarm-host")
	d.SwarmDiscovery = flags.String("swarm-discovery")
	d.ISO = d.ResolveStorePath(isoFilename)
	d.SSHUser = flags.String("qemu-ssh-user")
	d.SSHPort = 22
	d.DiskPath = d.ResolveStorePath(fmt.Sprintf("%s.img", d.MachineName))
	return nil
}

func (d *Driver) GetURL() (string, error) {
	log.Debugf("GetURL called")
	ip, err := d.GetIP()
	if err != nil {
		log.Warnf("Failed to get IP: %s", err)
		return "", err
	}
	if ip == "" {
		return "", nil
	}
	return fmt.Sprintf("tcp://%s:2376", ip), nil // TODO - don't hardcode the port!
}

func NewDriver(hostName, storePath string) drivers.Driver {
	return &Driver{
		PrivateNetwork: privateNetworkName,
		BaseDriver: &drivers.BaseDriver{
			SSHUser:     defaultSSHUser,
			MachineName: hostName,
			StorePath:   storePath,
		},
	}
}

func (d *Driver) GetIP() (string, error) {
	return "", nil
}

func (d *Driver) GetState() (state.State, error) {
	return state.None, nil
}

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

func (d *Driver) Create() error {
	var err error
	d.SSHPort, err = getAvailableTCPPort()
	if err != nil {
		return err
	}
	b2dutils := mcnutils.NewB2dUtils(d.StorePath)
	if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil {
		return err
	}

	log.Infof("Creating SSH key...")

	if err := ssh.GenerateSSHKey(d.sshKeyPath()); err != nil {
		return err
	}

	log.Infof("Creating Disk image...")

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

func getAvailableTCPPort() (int, error) {
	// FIXME: this has a race condition between finding an available port and
	// virtualbox using that port. Perhaps we should randomly pick an unused
	// port in a range not used by kernel for assigning ports
	ln, err := net.Listen("tcp4", "127.0.0.1:0")
	if err != nil {
		return 0, err
	}
	defer ln.Close()
	addr := ln.Addr().String()
	addrParts := strings.SplitN(addr, ":", 2)
	return strconv.Atoi(addrParts[1])
}

func (d *Driver) Start() error {
	// fmt.Printf("Init qemu %s\n", i.VM)

	// qemu-system-x86_64 -net nic -net user -m 2048M -boot d -cdrom boot2docker.iso persist.raw

	// qemu-system-x86_64 -enable-kvm -m 4096 -cdrom boot2docker.iso -boot order=d -net nic,vlan=0 -net user,vlan=0 -serial stdio

	// http://en.wikibooks.org/wiki/QEMU/Networking <<<< and samba sharing directly..

	startCmd := []string{
		// TODO add as param		"-curses",
		// "-enable-kvm", // need to test to see if its available
		"-netdev", "user,id=network0",
		"-device", "e1000,netdev=network0",
		"-redir", fmt.Sprintf("tcp:%d::22", d.SSHPort),
		"-m", fmt.Sprintf("%d", d.Memory),
		"-boot", "d",
		"-cdrom", "./boot2docker.iso",
		d.diskPath(),
	}

	if stdout, stderr, err := cmdOutErr("qemu-system-x86_64", startCmd...); err != nil {
		fmt.Printf("OUTPUT: %s\n", stdout)
		fmt.Printf("ERROR: %s\n", stderr)
		//	if err := cmdStart("qemu-system-x86_64", startCmd...); err != nil {
		return err
	}
	log.Infof("Waiting for VM to start (ssh -p %d docker@localhost)...", d.SSHPort)

	return nil
	//return ssh.WaitForTCP(fmt.Sprintf("localhost:%d", d.SSHPort))
}

func cmdOutErr(cmdStr string, args ...string) (string, string, error) {
	cmd := exec.Command(cmdStr, args...)
	log.Debugf("executing: %v %v", cmdStr, strings.Join(args, " "))
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	stderrStr := stderr.String()
	log.Debugf("STDOUT: %v", stdout.String())
	log.Debugf("STDERR: %v", stderrStr)
	if err != nil {
		if ee, ok := err.(*exec.Error); ok && ee == exec.ErrNotFound {
			err = fmt.Errorf("Mystery!")
		}
	} else {
		// VBoxManage will sometimes not set the return code, but has a fatal error
		// such as VBoxManage.exe: error: VT-x is not available. (VERR_VMX_NO_VMX)
		if strings.Contains(stderrStr, "error:") {
			err = fmt.Errorf("%v %v failed: %v", cmdStr, strings.Join(args, " "), stderrStr)
		}
	}
	return stdout.String(), stderrStr, err
}

func cmdStart(cmdStr string, args ...string) error {
	cmd := exec.Command(cmdStr, args...)
	log.Debugf("executing: %v %v", cmdStr, strings.Join(args, " "))
	return cmd.Start()
}

func (d *Driver) Stop() error {
	return fmt.Errorf("hosts without a driver cannot be stopped")
}

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

func (d *Driver) Restart() error {
	return fmt.Errorf("hosts without a driver cannot be restarted")
}

func (d *Driver) Kill() error {
	return fmt.Errorf("hosts without a driver cannot be killed")
}

func (d *Driver) StartDocker() error {
	return fmt.Errorf("hosts without a driver cannot start docker")
}

func (d *Driver) StopDocker() error {
	return fmt.Errorf("hosts without a driver cannot stop docker")
}

func (d *Driver) GetDockerConfigDir() string {
	return ""
}

func (d *Driver) Upgrade() error {
	return fmt.Errorf("hosts without a driver cannot be upgraded")
}

//func (d *Driver) GetSSHCommand(args ...string) (*exec.Cmd, error) {
//	return ssh.GetSSHCommand("localhost", d.SSHPort, "docker", d.sshKeyPath(), args...), nil
//}

func (d *Driver) sshKeyPath() string {
	return filepath.Join(d.StorePath, "id_rsa")
}

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

func (d *Driver) diskPath() string {
	return filepath.Join(d.StorePath, "disk.qcow2")
}

// Make a boot2docker VM disk image.
func (d *Driver) generateDiskImage(size int) error {
	log.Debugf("Creating %d MB hard disk image...", size)

	magicString := "boot2docker, please format-me"

	buf := new(bytes.Buffer)
	tw := tar.NewWriter(buf)

	// magicString first so the automount script knows to format the disk
	file := &tar.Header{Name: magicString, Size: int64(len(magicString))}
	if err := tw.WriteHeader(file); err != nil {
		return err
	}
	log.Infof("1")
	if _, err := tw.Write([]byte(magicString)); err != nil {
		return err
	}
	// .ssh/key.pub => authorized_keys
	log.Infof("2")
	file = &tar.Header{Name: ".ssh", Typeflag: tar.TypeDir, Mode: 0700}
	if err := tw.WriteHeader(file); err != nil {
		return err
	}
	log.Infof("3")
	pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath())
	if err != nil {
		return err
	}
	log.Infof("4")
	file = &tar.Header{Name: ".ssh/authorized_keys", Size: int64(len(pubKey)), Mode: 0644}
	if err := tw.WriteHeader(file); err != nil {
		return err
	}
	log.Infof("5")
	if _, err := tw.Write([]byte(pubKey)); err != nil {
		return err
	}
	log.Infof("6")
	file = &tar.Header{Name: ".ssh/authorized_keys2", Size: int64(len(pubKey)), Mode: 0644}
	if err := tw.WriteHeader(file); err != nil {
		return err
	}
	log.Infof("7")
	if _, err := tw.Write([]byte(pubKey)); err != nil {
		return err
	}
	log.Infof("8")
	if err := tw.Close(); err != nil {
		return err
	}
	log.Infof("9")
	rawFile := fmt.Sprintf("%s.raw", d.diskPath())
	if err := ioutil.WriteFile(rawFile, buf.Bytes(), 0644); err != nil {
		return nil
	}
	log.Infof("10")
	if stdout, stderr, err := cmdOutErr("qemu-img", "convert", "-f", "raw", "-O", "qcow2", rawFile, d.diskPath()); err != nil {
		fmt.Printf("OUTPUT: %s\n", stdout)
		fmt.Printf("ERROR: %s\n", stderr)
		return err
	}
	log.Infof("11")
	if stdout, stderr, err := cmdOutErr("qemu-img", "resize", d.diskPath(), fmt.Sprintf("+%dMB", size)); err != nil {
		fmt.Printf("OUTPUT: %s\n", stdout)
		fmt.Printf("ERROR: %s\n", stderr)
		return err
	}
	log.Debugf("DONE writing to %s and %s", rawFile, d.diskPath())

	return nil
}
<commit_msg>go lint<commit_after>package qemu

import (
	"archive/tar"
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/mcnflag"
	"github.com/docker/machine/libmachine/mcnutils"
	"github.com/docker/machine/libmachine/ssh"
	"github.com/docker/machine/libmachine/state"
)

const (
	isoFilename        = "boot2docker.iso"
	privateNetworkName = "docker-machines"

	defaultSSHUser = "docker"
)

type Driver struct {
	*drivers.BaseDriver

	Memory           int
	DiskSize         int
	CPU              int
	Network          string
	PrivateNetwork   string
	ISO              string
	Boot2DockerURL   string
	CaCertPath       string
	PrivateKeyPath   string
	DiskPath         string
	CacheMode        string
	IOMode           string
	connectionString string
	//	conn             *libvirt.Connect
	//	VM               *libvirt.Domain
	vmLoaded bool
}

func (d *Driver) GetCreateFlags() []mcnflag.Flag {
	return []mcnflag.Flag{
		mcnflag.IntFlag{
			Name:  "qemu-memory",
			Usage: "Size of memory for host in MB",
			Value: 1024,
		},
		mcnflag.IntFlag{
			Name:  "qemu-disk-size",
			Usage: "Size of disk for host in MB",
			Value: 20000,
		},
		mcnflag.IntFlag{
			Name:  "qemu-cpu-count",
			Usage: "Number of CPUs",
			Value: 1,
		},
		// TODO - support for multiple networks
		mcnflag.StringFlag{
			Name:  "qemu-network",
			Usage: "Name of network to connect to",
			Value: "default",
		},
		mcnflag.StringFlag{
			EnvVar: "KVM_BOOT2DOCKER_URL",
			Name:   "qemu-boot2docker-url",
			Usage:  "The URL of the boot2docker image. Defaults to the latest available version",
			Value:  "",
		},
		mcnflag.StringFlag{
			Name:  "qemu-cache-mode",
			Usage: "Disk cache mode: default, none, writethrough, writeback, directsync, or unsafe",
			Value: "default",
		},
		mcnflag.StringFlag{
			Name:  "qemu-io-mode",
			Usage: "Disk IO mode: threads, native",
			Value: "threads",
		},
		mcnflag.StringFlag{
			EnvVar: "KVM_SSH_USER",
			Name:   "qemu-ssh-user",
			Usage:  "SSH username",
			Value:  defaultSSHUser,
		},
		/* Not yet implemented
		mcnflag.Flag{
			Name:  "qemu-no-share",
			Usage: "Disable the mount of your home directory",
		},
		*/
	}
}

func (d *Driver) GetMachineName() string {
	return d.MachineName
}

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

func (d *Driver) GetSSHKeyPath() string {
	return d.ResolveStorePath("id_rsa")
}

func (d *Driver) GetSSHPort() (int, error) {
	if d.SSHPort == 0 {
		d.SSHPort = 22
	}

	return d.SSHPort, nil
}

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

	return d.SSHUser
}

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

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
	log.Debugf("SetConfigFromFlags called")
	d.Memory = flags.Int("qemu-memory")
	d.DiskSize = flags.Int("qemu-disk-size")
	d.CPU = flags.Int("qemu-cpu-count")
	d.Network = flags.String("qemu-network")
	d.Boot2DockerURL = flags.String("qemu-boot2docker-url")
	d.CacheMode = flags.String("qemu-cache-mode")
	d.IOMode = flags.String("qemu-io-mode")

	d.SwarmMaster = flags.Bool("swarm-master")
	d.SwarmHost = flags.String("swarm-host")
	d.SwarmDiscovery = flags.String("swarm-discovery")
	d.ISO = d.ResolveStorePath(isoFilename)
	d.SSHUser = flags.String("qemu-ssh-user")
	d.SSHPort = 22
	d.DiskPath = d.ResolveStorePath(fmt.Sprintf("%s.img", d.MachineName))
	return nil
}

func (d *Driver) GetURL() (string, error) {
	log.Debugf("GetURL called")
	ip, err := d.GetIP()
	if err != nil {
		log.Warnf("Failed to get IP: %s", err)
		return "", err
	}
	if ip == "" {
		return "", nil
	}
	return fmt.Sprintf("tcp://%s:2376", ip), nil // TODO - don't hardcode the port!
}

func NewDriver(hostName, storePath string) drivers.Driver {
	return &Driver{
		PrivateNetwork: privateNetworkName,
		BaseDriver: &drivers.BaseDriver{
			SSHUser:     defaultSSHUser,
			MachineName: hostName,
			StorePath:   storePath,
		},
	}
}

func (d *Driver) GetIP() (string, error) {
	return "", nil
}

func (d *Driver) GetState() (state.State, error) {
	return state.None, nil
}

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

func (d *Driver) Create() error {
	var err error
	d.SSHPort, err = getAvailableTCPPort()
	if err != nil {
		return err
	}
	b2dutils := mcnutils.NewB2dUtils(d.StorePath)
	if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil {
		return err
	}

	log.Infof("Creating SSH key...")

	if err := ssh.GenerateSSHKey(d.sshKeyPath()); err != nil {
		return err
	}

	log.Infof("Creating Disk image...")

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

func getAvailableTCPPort() (int, error) {
	// FIXME: this has a race condition between finding an available port and
	// virtualbox using that port. Perhaps we should randomly pick an unused
	// port in a range not used by kernel for assigning ports
	ln, err := net.Listen("tcp4", "127.0.0.1:0")
	if err != nil {
		return 0, err
	}
	defer ln.Close()
	addr := ln.Addr().String()
	addrParts := strings.SplitN(addr, ":", 2)
	return strconv.Atoi(addrParts[1])
}

func (d *Driver) Start() error {
	// fmt.Printf("Init qemu %s\n", i.VM)

	// qemu-system-x86_64 -net nic -net user -m 2048M -boot d -cdrom boot2docker.iso persist.raw

	// qemu-system-x86_64 -enable-kvm -m 4096 -cdrom boot2docker.iso -boot order=d -net nic,vlan=0 -net user,vlan=0 -serial stdio

	// http://en.wikibooks.org/wiki/QEMU/Networking <<<< and samba sharing directly..

	startCmd := []string{
		// TODO add as param		"-curses",
		// "-enable-kvm", // need to test to see if its available
		"-netdev", "user,id=network0",
		"-device", "e1000,netdev=network0",
		"-redir", fmt.Sprintf("tcp:%d::22", d.SSHPort),
		"-m", fmt.Sprintf("%d", d.Memory),
		"-boot", "d",
		"-cdrom", "./boot2docker.iso",
		d.diskPath(),
	}

	if stdout, stderr, err := cmdOutErr("qemu-system-x86_64", startCmd...); err != nil {
		fmt.Printf("OUTPUT: %s\n", stdout)
		fmt.Printf("ERROR: %s\n", stderr)
		//	if err := cmdStart("qemu-system-x86_64", startCmd...); err != nil {
		return err
	}
	log.Infof("Waiting for VM to start (ssh -p %d docker@localhost)...", d.SSHPort)

	return nil
	//return ssh.WaitForTCP(fmt.Sprintf("localhost:%d", d.SSHPort))
}

func cmdOutErr(cmdStr string, args ...string) (string, string, error) {
	cmd := exec.Command(cmdStr, args...)
	log.Debugf("executing: %v %v", cmdStr, strings.Join(args, " "))
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	stderrStr := stderr.String()
	log.Debugf("STDOUT: %v", stdout.String())
	log.Debugf("STDERR: %v", stderrStr)
	if err != nil {
		if ee, ok := err.(*exec.Error); ok && ee == exec.ErrNotFound {
			err = fmt.Errorf("mystery error: %s", ee)
		}
	} else {
		// VBoxManage will sometimes not set the return code, but has a fatal error
		// such as VBoxManage.exe: error: VT-x is not available. (VERR_VMX_NO_VMX)
		if strings.Contains(stderrStr, "error:") {
			err = fmt.Errorf("%v %v failed: %v", cmdStr, strings.Join(args, " "), stderrStr)
		}
	}
	return stdout.String(), stderrStr, err
}

func cmdStart(cmdStr string, args ...string) error {
	cmd := exec.Command(cmdStr, args...)
	log.Debugf("executing: %v %v", cmdStr, strings.Join(args, " "))
	return cmd.Start()
}

func (d *Driver) Stop() error {
	return fmt.Errorf("hosts without a driver cannot be stopped")
}

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

func (d *Driver) Restart() error {
	return fmt.Errorf("hosts without a driver cannot be restarted")
}

func (d *Driver) Kill() error {
	return fmt.Errorf("hosts without a driver cannot be killed")
}

func (d *Driver) StartDocker() error {
	return fmt.Errorf("hosts without a driver cannot start docker")
}

func (d *Driver) StopDocker() error {
	return fmt.Errorf("hosts without a driver cannot stop docker")
}

func (d *Driver) GetDockerConfigDir() string {
	return ""
}

func (d *Driver) Upgrade() error {
	return fmt.Errorf("hosts without a driver cannot be upgraded")
}

//func (d *Driver) GetSSHCommand(args ...string) (*exec.Cmd, error) {
//	return ssh.GetSSHCommand("localhost", d.SSHPort, "docker", d.sshKeyPath(), args...), nil
//}

func (d *Driver) sshKeyPath() string {
	return filepath.Join(d.StorePath, "id_rsa")
}

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

func (d *Driver) diskPath() string {
	return filepath.Join(d.StorePath, "disk.qcow2")
}

// Make a boot2docker VM disk image.
func (d *Driver) generateDiskImage(size int) error {
	log.Debugf("Creating %d MB hard disk image...", size)

	magicString := "boot2docker, please format-me"

	buf := new(bytes.Buffer)
	tw := tar.NewWriter(buf)

	// magicString first so the automount script knows to format the disk
	file := &tar.Header{Name: magicString, Size: int64(len(magicString))}
	if err := tw.WriteHeader(file); err != nil {
		return err
	}
	log.Infof("1")
	if _, err := tw.Write([]byte(magicString)); err != nil {
		return err
	}
	// .ssh/key.pub => authorized_keys
	log.Infof("2")
	file = &tar.Header{Name: ".ssh", Typeflag: tar.TypeDir, Mode: 0700}
	if err := tw.WriteHeader(file); err != nil {
		return err
	}
	log.Infof("3")
	pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath())
	if err != nil {
		return err
	}
	log.Infof("4")
	file = &tar.Header{Name: ".ssh/authorized_keys", Size: int64(len(pubKey)), Mode: 0644}
	if err := tw.WriteHeader(file); err != nil {
		return err
	}
	log.Infof("5")
	if _, err := tw.Write([]byte(pubKey)); err != nil {
		return err
	}
	log.Infof("6")
	file = &tar.Header{Name: ".ssh/authorized_keys2", Size: int64(len(pubKey)), Mode: 0644}
	if err := tw.WriteHeader(file); err != nil {
		return err
	}
	log.Infof("7")
	if _, err := tw.Write([]byte(pubKey)); err != nil {
		return err
	}
	log.Infof("8")
	if err := tw.Close(); err != nil {
		return err
	}
	log.Infof("9")
	rawFile := fmt.Sprintf("%s.raw", d.diskPath())
	if err := ioutil.WriteFile(rawFile, buf.Bytes(), 0644); err != nil {
		return nil
	}
	log.Infof("10")
	if stdout, stderr, err := cmdOutErr("qemu-img", "convert", "-f", "raw", "-O", "qcow2", rawFile, d.diskPath()); err != nil {
		fmt.Printf("OUTPUT: %s\n", stdout)
		fmt.Printf("ERROR: %s\n", stderr)
		return err
	}
	log.Infof("11")
	if stdout, stderr, err := cmdOutErr("qemu-img", "resize", d.diskPath(), fmt.Sprintf("+%dMB", size)); err != nil {
		fmt.Printf("OUTPUT: %s\n", stdout)
		fmt.Printf("ERROR: %s\n", stderr)
		return err
	}
	log.Debugf("DONE writing to %s and %s", rawFile, d.diskPath())

	return nil
}
<|endoftext|>


<commit_before>// +build appengine

package cellar

import (
	"net/http"
	"os"
	"regexp"

	"appengine"

	"github.com/raphael/goa"
	"github.com/raphael/goa/examples/cellar/controllers"
	"gopkg.in/inconshreveable/log15.v2"
)

func init() {
	goa.Log.SetHandler(log15.MultiHandler(
		log15.StreamHandler(os.Stderr, log15.LogfmtFormat()),
		AppEngineLogHandler()),
	)
	api := controllers.New()
	api.Use(AppEngineLogCtx())
	api.Use(goa.CORS(corsPath, "*", "", "", "", "GET", ""))
	controllers.Mount(api)
	http.HandleFunc("/", api.ServeHTTP)
}

// Format used for logging to AppEngine
var logFormat = log15.JsonFormat()

// Paths that must return CORS headers
var corsPath = regexp.MustCompile(`^/(schema|swagger)\.json$`)

// AppEngineLogHandler sends logs to AppEngine.
// The record must contain the appengine request context.
func AppEngineLogHandler() log15.Handler {
	return log15.FuncHandler(func(r *log15.Record) error {
		var c appengine.Context
		index := 0
		for i, e := range r.Ctx {
			if ct, ok := e.(appengine.Context); ok {
				c = ct
				index = i
				break
			}
		}
		if c == nil {
			// not in the context of a request
			return nil
		}
		r.Ctx = append(r.Ctx[:index-1], r.Ctx[index+1:]...)
		log := string(logFormat.Format(r))
		switch r.Lvl {
		case log15.LvlCrit:
			c.Criticalf(log)
		case log15.LvlError:
			c.Errorf(log)
		case log15.LvlWarn:
			c.Warningf(log)
		case log15.LvlInfo:
			c.Infof(log)
		case log15.LvlDebug:
			c.Debugf(log)
		}
		return nil
	})
}

// AppEngineLogCtx returns a goa middleware that sets the appengine context in the log records.
func AppEngineLogCtx() goa.Middleware {
	return func(h goa.Handler) goa.Handler {
		return func(ctx *goa.Context) error {
			actx := appengine.NewContext(ctx.Request())
			ctx.SetValue(goa.ReqIDKey, appengine.RequestID(actx))
			ctx.Logger = ctx.Logger.New("aeCtx", actx)
			return h(ctx)
		}
	}
}
<commit_msg>Update AppEngine cellar example<commit_after>// +build appengine

package cellar

import (
	"net/http"
	"os"
	"regexp"

	"appengine"

	"github.com/raphael/goa"
	"github.com/raphael/goa/examples/cellar/controllers"
	"gopkg.in/inconshreveable/log15.v2"
)

func init() {
	goa.Log.SetHandler(log15.MultiHandler(
		log15.StreamHandler(os.Stderr, log15.LogfmtFormat()),
		AppEngineLogHandler()),
	)
	service := controllers.New()
	service.Use(AppEngineLogCtx())
	service.Use(goa.CORS(corsPath, "*", "", "", "", "GET", ""))
	controllers.Mount(service)
	http.HandleFunc("/", service.HTTPHandler().ServeHTTP)
}

// Format used for logging to AppEngine
var logFormat = log15.JsonFormat()

// Paths that must return CORS headers
var corsPath = regexp.MustCompile(`^/(schema|swagger)\.json$`)

// AppEngineLogHandler sends logs to AppEngine.
// The record must contain the appengine request context.
func AppEngineLogHandler() log15.Handler {
	return log15.FuncHandler(func(r *log15.Record) error {
		var c appengine.Context
		index := 0
		for i, e := range r.Ctx {
			if ct, ok := e.(appengine.Context); ok {
				c = ct
				index = i
				break
			}
		}
		if c == nil {
			// not in the context of a request
			return nil
		}
		r.Ctx = append(r.Ctx[:index-1], r.Ctx[index+1:]...)
		log := string(logFormat.Format(r))
		switch r.Lvl {
		case log15.LvlCrit:
			c.Criticalf(log)
		case log15.LvlError:
			c.Errorf(log)
		case log15.LvlWarn:
			c.Warningf(log)
		case log15.LvlInfo:
			c.Infof(log)
		case log15.LvlDebug:
			c.Debugf(log)
		}
		return nil
	})
}

// AppEngineLogCtx returns a goa middleware that sets the appengine context in the log records.
func AppEngineLogCtx() goa.Middleware {
	return func(h goa.Handler) goa.Handler {
		return func(ctx *goa.Context) error {
			actx := appengine.NewContext(ctx.Request())
			ctx.SetValue(goa.ReqIDKey, appengine.RequestID(actx))
			ctx.Logger = ctx.Logger.New("aeCtx", actx)
			return h(ctx)
		}
	}
}
<|endoftext|>


<commit_before>package arts

import (
	"code.google.com/p/cascadia"
	"errors"
	"fmt"
	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
	"regexp"
	"sort"
	"strings"
)

var headlinePats = struct {
	considerSel         cascadia.Selector // elements to consider
	titleSel            cascadia.Selector
	metaTitlesSel       cascadia.Selector // meta tags which have title
	itemPropHeadlineSel cascadia.Selector
	indicativePat       *regexp.Regexp
	unIndicativePat     *regexp.Regexp
}{
	cascadia.MustCompile("h1,h2,h3,h4,h5,h6,div,span,th,td"),
	cascadia.MustCompile("title"),
	cascadia.MustCompile(`meta[property="og:title"], meta[name="wp_twitter-title"]`),
	cascadia.MustCompile(`[itemprop="headline"]`),
	regexp.MustCompile(`(?i)entry-title|headline|title`),
	regexp.MustCompile(`(?i)feed-title`),
}

func grabHeadline(root *html.Node, art_url string) (string, *html.Node, error) {
	dbug := Debug.HeadlineLogger

	var candidates = make(candidateList, 0, 100)

	cookedSlug := toAlphanumeric(regexp.MustCompile("[-_]+").ReplaceAllLiteralString(getSlug(art_url), " "))
	dbug.Printf("slug: '%s'\n", cookedSlug)

	//html.Render(os.Stderr, root)
	//dumpTree(root, 0)

	var cookedTitle string
	t := headlinePats.titleSel.MatchFirst(root)
	if t != nil {
		cookedTitle = toAlphanumeric(getTextContent(t))
	}

	// check for any interesting meta tags (og:title etc...)
	// remember that some sites append the site name to this (eg telegraph,
	// rolling stone) so we can't just take it verbatim. But it gives us clues...
	type metaTitle struct {
		cooked string
		node   *html.Node
	}
	metaTitles := []metaTitle{}
	for _, metaTitleNode := range headlinePats.metaTitlesSel.MatchAll(root) {
		cooked := toAlphanumeric(getAttr(metaTitleNode, "content"))
		metaTitles = append(metaTitles, metaTitle{cooked, metaTitleNode})
	}

	// TODO: early-out on hatom or schema.org article

	for _, el := range headlinePats.considerSel.MatchAll(root) {

		txt := compressSpace(getTextContent(el))
		if len(txt) >= 500 {
			continue // too long
		}
		if len(txt) < 3 {
			continue // too short
		}

		cookedTxt := toAlphanumeric(txt)

		c := newStandardCandidate(el, txt)

		// TEST: schema.org headline
		if headlinePats.itemPropHeadlineSel.Match(el) {
			c.addPoints(2, `itemprop="headline"`)
		}

		// TEST: is it a headliney element?
		tag := el.DataAtom
		if tag == atom.H1 || tag == atom.H2 || tag == atom.H3 || tag == atom.H4 {
			c.addPoints(2, "headliney")
		}
		if tag == atom.Span || tag == atom.Td {
			c.addPoints(-2, "not headliney")
		}

		// TEST: likely-looking class or id?
		cls := getAttr(el, "class")
		id := getAttr(el, "id")
		if cls != "" && (headlinePats.indicativePat.FindStringIndex(cls) != nil) {
			c.addPoints(2, "indicative class")
		}
		if id != "" && (headlinePats.indicativePat.FindStringIndex(id) != nil) {
			c.addPoints(2, "indicative id")
		}

		// TEST: unlikely-looking class or id?
		if cls != "" && (headlinePats.unIndicativePat.FindStringIndex(cls) != nil) {
			c.addPoints(-1, "unindicative class")
		}
		if id != "" && (headlinePats.unIndicativePat.FindStringIndex(id) != nil) {
			c.addPoints(-1, "unindicative id")
		}
		if len(cookedTxt) > 0 {
			// TEST: beginning of <title>?
			if strings.HasPrefix(cookedTitle, cookedTxt) {
				c.addPoints(2, "appears at start of <title>")
			}

			if wordCount(cookedTxt) >= 3 {

				// TEST: appears in page <title>?
				{
					value := jaccardWordCompare(cookedTxt, cookedTitle)
					c.addPoints((value*4)-1, "score against <title>")
				}

				// TEST: like the slug?
				if cookedSlug != "" {
					value := jaccardWordCompare(cookedTxt, cookedSlug)
					c.addPoints((value*4)-1, "score against slug")
				}
			}

			// TEST: likely-looking meta tags
			for _, metaTitle := range metaTitles {
				value := jaccardWordCompare(cookedTxt, metaTitle.cooked)
				c.addPoints((value*4)-1, fmt.Sprintf("score against %s", describeNode(metaTitle.node)))
			}
		}

		// TEST: inside an obvious sidebar or <aside>?

		/* TODO!!!!!!!!!!!!!!!!!!!!!!!
		if s.Closest("aside").Length() > 0 {
			c.addPoints(-3, "contained within <aside>")
		}
		if s.Closest("#sidebar").Length() > 0 {
			c.addPoints(-3, "contained within #sidebar")
		}

		// TEST: within article container?
		if insideArticle(s) {
			c.addPoints(1, "within article container")
		}
		*/

		// IDEAS:
		//  promote if within <article> <header>?

		if c.total() > 0 {
			candidates = append(candidates, c)
		}
	}

	sort.Sort(Reverse{candidates})

	dbug.Printf("HEADLINE %d candidates\n", len(candidates))
	// show the top ten, with reasons
	//	if len(candidates) > 20 {
	//		candidates = candidates[0:20]
	//	}
	for _, c := range candidates {
		c.dump(dbug)
	}

	if len(candidates) > 0 {
		return candidates[0].txt(), candidates[0].node(), nil
	}
	return "", nil, errors.New("couldn't find a headline")
}

/*
func insideArticle(s *goquery.Selection) bool {
	if s.Closest("article, #post, .article, .story-body").Length() > 0 {
		return true
	}

	return false
}
*/
<commit_msg>headline: trim out obvious date cruft in headlines<commit_after>package arts

import (
	"code.google.com/p/cascadia"
	"errors"
	"fmt"
	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
	"regexp"
	"sort"
	"strings"
)

var headlinePats = struct {
	considerSel         cascadia.Selector // elements to consider
	titleSel            cascadia.Selector
	metaTitlesSel       cascadia.Selector // meta tags which have title
	itemPropHeadlineSel cascadia.Selector
	indicativePat       *regexp.Regexp
	unIndicativePat     *regexp.Regexp
}{
	cascadia.MustCompile("h1,h2,h3,h4,h5,h6,div,span,th,td"),
	cascadia.MustCompile("title"),
	cascadia.MustCompile(`meta[property="og:title"], meta[name="wp_twitter-title"]`),
	cascadia.MustCompile(`[itemprop="headline"]`),
	regexp.MustCompile(`(?i)entry-title|headline|title`),
	regexp.MustCompile(`(?i)feed-title`),
}

func grabHeadline(root *html.Node, art_url string) (string, *html.Node, error) {
	dbug := Debug.HeadlineLogger

	var candidates = make(candidateList, 0, 100)

	cookedSlug := toAlphanumeric(regexp.MustCompile("[-_]+").ReplaceAllLiteralString(getSlug(art_url), " "))
	dbug.Printf("slug: '%s'\n", cookedSlug)

	//html.Render(os.Stderr, root)
	//dumpTree(root, 0)

	var cookedTitle string
	t := headlinePats.titleSel.MatchFirst(root)
	if t != nil {
		cookedTitle = toAlphanumeric(getTextContent(t))
	}

	// check for any interesting meta tags (og:title etc...)
	// remember that some sites append the site name to this (eg telegraph,
	// rolling stone) so we can't just take it verbatim. But it gives us clues...
	type metaTitle struct {
		cooked string
		node   *html.Node
	}
	metaTitles := []metaTitle{}
	for _, metaTitleNode := range headlinePats.metaTitlesSel.MatchAll(root) {
		cooked := toAlphanumeric(getAttr(metaTitleNode, "content"))
		metaTitles = append(metaTitles, metaTitle{cooked, metaTitleNode})
	}

	// TODO: early-out on hatom or schema.org article

	for _, el := range headlinePats.considerSel.MatchAll(root) {

		txt := compressSpace(getTextContent(el))
		if len(txt) >= 500 {
			continue // too long
		}
		if len(txt) < 3 {
			continue // too short
		}

		cookedTxt := toAlphanumeric(txt)

		c := newStandardCandidate(el, txt)

		// TEST: schema.org headline
		if headlinePats.itemPropHeadlineSel.Match(el) {
			c.addPoints(2, `itemprop="headline"`)
		}

		// TEST: is it a headliney element?
		tag := el.DataAtom
		if tag == atom.H1 || tag == atom.H2 || tag == atom.H3 || tag == atom.H4 {
			c.addPoints(2, "headliney")
		}
		if tag == atom.Span || tag == atom.Td {
			c.addPoints(-2, "not headliney")
		}

		// TEST: likely-looking class or id?
		cls := getAttr(el, "class")
		id := getAttr(el, "id")
		if cls != "" && (headlinePats.indicativePat.FindStringIndex(cls) != nil) {
			c.addPoints(2, "indicative class")
		}
		if id != "" && (headlinePats.indicativePat.FindStringIndex(id) != nil) {
			c.addPoints(2, "indicative id")
		}

		// TEST: unlikely-looking class or id?
		if cls != "" && (headlinePats.unIndicativePat.FindStringIndex(cls) != nil) {
			c.addPoints(-1, "unindicative class")
		}
		if id != "" && (headlinePats.unIndicativePat.FindStringIndex(id) != nil) {
			c.addPoints(-1, "unindicative id")
		}
		if len(cookedTxt) > 0 {
			// TEST: beginning of <title>?
			if strings.HasPrefix(cookedTitle, cookedTxt) {
				c.addPoints(2, "appears at start of <title>")
			}

			if wordCount(cookedTxt) >= 3 {

				// TEST: appears in page <title>?
				{
					value := jaccardWordCompare(cookedTxt, cookedTitle)
					c.addPoints((value*4)-1, "score against <title>")
				}

				// TEST: like the slug?
				if cookedSlug != "" {
					value := jaccardWordCompare(cookedTxt, cookedSlug)
					c.addPoints((value*4)-1, "score against slug")
				}
			}

			// TEST: likely-looking meta tags
			for _, metaTitle := range metaTitles {
				value := jaccardWordCompare(cookedTxt, metaTitle.cooked)
				c.addPoints((value*4)-1, fmt.Sprintf("score against %s", describeNode(metaTitle.node)))
			}
		}

		// TEST: inside an obvious sidebar or <aside>?

		/* TODO!!!!!!!!!!!!!!!!!!!!!!!
		if s.Closest("aside").Length() > 0 {
			c.addPoints(-3, "contained within <aside>")
		}
		if s.Closest("#sidebar").Length() > 0 {
			c.addPoints(-3, "contained within #sidebar")
		}

		// TEST: within article container?
		if insideArticle(s) {
			c.addPoints(1, "within article container")
		}
		*/

		// IDEAS:
		//  promote if within <article> <header>?

		if c.total() > 0 {
			candidates = append(candidates, c)
		}
	}

	sort.Sort(Reverse{candidates})

	dbug.Printf("HEADLINE %d candidates\n", len(candidates))
	// show the top ten, with reasons
	//	if len(candidates) > 20 {
	//		candidates = candidates[0:20]
	//	}
	for _, c := range candidates {
		c.dump(dbug)
	}

	if len(candidates) > 0 {
		headline := compressSpace(headlineText(candidates[0].node()))
		return headline, candidates[0].node(), nil
	}
	return "", nil, errors.New("couldn't find a headline")
}

// get text for a headline, stripping obviously-wrong elements
func headlineText(n *html.Node) string {
	if n.Type == html.TextNode {
		return n.Data
	}

	// sometimes have timestamps within headline...
	cls := getAttr(n, "class") + " " + getAttr(n, "id")
	if datePats.genericClasses.MatchString(cls) ||
		datePats.publishedClasses.MatchString(cls) ||
		datePats.updatedClasses.MatchString(cls) {
		return ""
	}

	txt := ""
	for child := n.FirstChild; child != nil; child = child.NextSibling {
		txt += headlineText(child)
	}

	return txt
}
<|endoftext|>


<commit_before>// +build linux

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"runtime"

	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/urfave/cli"
)

var specCommand = cli.Command{
	Name:      "spec",
	Usage:     "create a new specification file",
	ArgsUsage: "",
	Description: `The spec command creates the new specification file named "` + specConfig + `" for
the bundle.

The spec generated is just a starter file. Editing of the spec is required to
achieve desired results. For example, the newly generated spec includes an args
parameter that is initially set to call the "sh" command when the container is
started. Calling "sh" may work for an ubuntu container or busybox, but will not
work for containers that do not include the "sh" program.

EXAMPLE:
  To run docker's hello-world container one needs to set the args parameter
in the spec to call hello. This can be done using the sed command or a text
editor. The following commands create a bundle for hello-world, change the
default args parameter in the spec from "sh" to "/hello", then run the hello
command in a new hello-world container named container1:

    mkdir hello
    cd hello
    docker pull hello-world
    docker export $(docker create hello-world) > hello-world.tar
    mkdir rootfs
    tar -C rootfs -xf hello-world.tar
    runc spec
    sed -i 's;"sh";"/hello";' ` + specConfig + `
    runc run container1

In the run command above, "container1" is the name for the instance of the
container that you are starting. The name you provide for the container instance
must be unique on your host.

An alternative for generating a customized spec config is to use "ocitools", the
sub-command "ocitools generate" has lots of options that can be used to do any
customizations as you want, see [ocitools](https://github.com/opencontainers/ocitools)
to get more information.

When starting a container through runc, runc needs root privilege. If not
already running as root, you can use sudo to give runc root privilege. For
example: "sudo runc start container1" will give runc root privilege to start the
container on your host.`,
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "bundle, b",
			Value: "",
			Usage: "path to the root of the bundle directory",
		},
	},
	Action: func(context *cli.Context) error {
		spec := specs.Spec{
			Version: specs.Version,
			Platform: specs.Platform{
				OS:   runtime.GOOS,
				Arch: runtime.GOARCH,
			},
			Root: specs.Root{
				Path:     "rootfs",
				Readonly: true,
			},
			Process: specs.Process{
				Terminal: true,
				User:     specs.User{},
				Args: []string{
					"sh",
				},
				Env: []string{
					"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
					"TERM=xterm",
				},
				Cwd:             "/",
				NoNewPrivileges: true,
				Capabilities: []string{
					"CAP_AUDIT_WRITE",
					"CAP_KILL",
					"CAP_NET_BIND_SERVICE",
				},
				Rlimits: []specs.Rlimit{
					{
						Type: "RLIMIT_NOFILE",
						Hard: uint64(1024),
						Soft: uint64(1024),
					},
				},
			},
			Hostname: "runc",
			Mounts: []specs.Mount{
				{
					Destination: "/proc",
					Type:        "proc",
					Source:      "proc",
					Options:     nil,
				},
				{
					Destination: "/dev",
					Type:        "tmpfs",
					Source:      "tmpfs",
					Options:     []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
				},
				{
					Destination: "/dev/pts",
					Type:        "devpts",
					Source:      "devpts",
					Options:     []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
				},
				{
					Destination: "/dev/shm",
					Type:        "tmpfs",
					Source:      "shm",
					Options:     []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"},
				},
				{
					Destination: "/dev/mqueue",
					Type:        "mqueue",
					Source:      "mqueue",
					Options:     []string{"nosuid", "noexec", "nodev"},
				},
				{
					Destination: "/sys",
					Type:        "sysfs",
					Source:      "sysfs",
					Options:     []string{"nosuid", "noexec", "nodev", "ro"},
				},
				{
					Destination: "/sys/fs/cgroup",
					Type:        "cgroup",
					Source:      "cgroup",
					Options:     []string{"nosuid", "noexec", "nodev", "relatime", "ro"},
				},
			},
			Linux: specs.Linux{
				MaskedPaths: []string{
					"/proc/kcore",
					"/proc/latency_stats",
					"/proc/timer_stats",
					"/proc/sched_debug",
				},
				ReadonlyPaths: []string{
					"/proc/asound",
					"/proc/bus",
					"/proc/fs",
					"/proc/irq",
					"/proc/sys",
					"/proc/sysrq-trigger",
				},
				Resources: &specs.Resources{
					Devices: []specs.DeviceCgroup{
						{
							Allow:  false,
							Access: sPtr("rwm"),
						},
					},
				},
				Namespaces: []specs.Namespace{
					{
						Type: "pid",
					},
					{
						Type: "network",
					},
					{
						Type: "ipc",
					},
					{
						Type: "uts",
					},
					{
						Type: "mount",
					},
				},
			},
		}

		checkNoFile := func(name string) error {
			_, err := os.Stat(name)
			if err == nil {
				return fmt.Errorf("File %s exists. Remove it first", name)
			}
			if !os.IsNotExist(err) {
				return err
			}
			return nil
		}
		bundle := context.String("bundle")
		if bundle != "" {
			if err := os.Chdir(bundle); err != nil {
				return err
			}
		}
		if err := checkNoFile(specConfig); err != nil {
			return err
		}
		data, err := json.MarshalIndent(&spec, "", "\t")
		if err != nil {
			return err
		}
		if err := ioutil.WriteFile(specConfig, data, 0666); err != nil {
			return err
		}
		return nil
	},
}

func sPtr(s string) *string      { return &s }
func rPtr(r rune) *rune          { return &r }
func iPtr(i int64) *int64        { return &i }
func u32Ptr(i int64) *uint32     { u := uint32(i); return &u }
func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm }

// loadSpec loads the specification from the provided path.
func loadSpec(cPath string) (spec *specs.Spec, err error) {
	cf, err := os.Open(cPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("JSON specification file %s not found", cPath)
		}
		return nil, err
	}
	defer cf.Close()

	if err = json.NewDecoder(cf).Decode(&spec); err != nil {
		return nil, err
	}
	return spec, validateProcessSpec(&spec.Process)
}

func createLibContainerRlimit(rlimit specs.Rlimit) (configs.Rlimit, error) {
	rl, err := strToRlimit(rlimit.Type)
	if err != nil {
		return configs.Rlimit{}, err
	}
	return configs.Rlimit{
		Type: rl,
		Hard: uint64(rlimit.Hard),
		Soft: uint64(rlimit.Soft),
	}, nil
}
<commit_msg>Adding /proc/timer_list to the masked paths list<commit_after>// +build linux

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"runtime"

	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/urfave/cli"
)

var specCommand = cli.Command{
	Name:      "spec",
	Usage:     "create a new specification file",
	ArgsUsage: "",
	Description: `The spec command creates the new specification file named "` + specConfig + `" for
the bundle.

The spec generated is just a starter file. Editing of the spec is required to
achieve desired results. For example, the newly generated spec includes an args
parameter that is initially set to call the "sh" command when the container is
started. Calling "sh" may work for an ubuntu container or busybox, but will not
work for containers that do not include the "sh" program.

EXAMPLE:
  To run docker's hello-world container one needs to set the args parameter
in the spec to call hello. This can be done using the sed command or a text
editor. The following commands create a bundle for hello-world, change the
default args parameter in the spec from "sh" to "/hello", then run the hello
command in a new hello-world container named container1:

    mkdir hello
    cd hello
    docker pull hello-world
    docker export $(docker create hello-world) > hello-world.tar
    mkdir rootfs
    tar -C rootfs -xf hello-world.tar
    runc spec
    sed -i 's;"sh";"/hello";' ` + specConfig + `
    runc run container1

In the run command above, "container1" is the name for the instance of the
container that you are starting. The name you provide for the container instance
must be unique on your host.

An alternative for generating a customized spec config is to use "ocitools", the
sub-command "ocitools generate" has lots of options that can be used to do any
customizations as you want, see [ocitools](https://github.com/opencontainers/ocitools)
to get more information.

When starting a container through runc, runc needs root privilege. If not
already running as root, you can use sudo to give runc root privilege. For
example: "sudo runc start container1" will give runc root privilege to start the
container on your host.`,
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "bundle, b",
			Value: "",
			Usage: "path to the root of the bundle directory",
		},
	},
	Action: func(context *cli.Context) error {
		spec := specs.Spec{
			Version: specs.Version,
			Platform: specs.Platform{
				OS:   runtime.GOOS,
				Arch: runtime.GOARCH,
			},
			Root: specs.Root{
				Path:     "rootfs",
				Readonly: true,
			},
			Process: specs.Process{
				Terminal: true,
				User:     specs.User{},
				Args: []string{
					"sh",
				},
				Env: []string{
					"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
					"TERM=xterm",
				},
				Cwd:             "/",
				NoNewPrivileges: true,
				Capabilities: []string{
					"CAP_AUDIT_WRITE",
					"CAP_KILL",
					"CAP_NET_BIND_SERVICE",
				},
				Rlimits: []specs.Rlimit{
					{
						Type: "RLIMIT_NOFILE",
						Hard: uint64(1024),
						Soft: uint64(1024),
					},
				},
			},
			Hostname: "runc",
			Mounts: []specs.Mount{
				{
					Destination: "/proc",
					Type:        "proc",
					Source:      "proc",
					Options:     nil,
				},
				{
					Destination: "/dev",
					Type:        "tmpfs",
					Source:      "tmpfs",
					Options:     []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
				},
				{
					Destination: "/dev/pts",
					Type:        "devpts",
					Source:      "devpts",
					Options:     []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
				},
				{
					Destination: "/dev/shm",
					Type:        "tmpfs",
					Source:      "shm",
					Options:     []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"},
				},
				{
					Destination: "/dev/mqueue",
					Type:        "mqueue",
					Source:      "mqueue",
					Options:     []string{"nosuid", "noexec", "nodev"},
				},
				{
					Destination: "/sys",
					Type:        "sysfs",
					Source:      "sysfs",
					Options:     []string{"nosuid", "noexec", "nodev", "ro"},
				},
				{
					Destination: "/sys/fs/cgroup",
					Type:        "cgroup",
					Source:      "cgroup",
					Options:     []string{"nosuid", "noexec", "nodev", "relatime", "ro"},
				},
			},
			Linux: specs.Linux{
				MaskedPaths: []string{
					"/proc/kcore",
					"/proc/latency_stats",
					"/proc/timer_list",
					"/proc/timer_stats",
					"/proc/sched_debug",
				},
				ReadonlyPaths: []string{
					"/proc/asound",
					"/proc/bus",
					"/proc/fs",
					"/proc/irq",
					"/proc/sys",
					"/proc/sysrq-trigger",
				},
				Resources: &specs.Resources{
					Devices: []specs.DeviceCgroup{
						{
							Allow:  false,
							Access: sPtr("rwm"),
						},
					},
				},
				Namespaces: []specs.Namespace{
					{
						Type: "pid",
					},
					{
						Type: "network",
					},
					{
						Type: "ipc",
					},
					{
						Type: "uts",
					},
					{
						Type: "mount",
					},
				},
			},
		}

		checkNoFile := func(name string) error {
			_, err := os.Stat(name)
			if err == nil {
				return fmt.Errorf("File %s exists. Remove it first", name)
			}
			if !os.IsNotExist(err) {
				return err
			}
			return nil
		}
		bundle := context.String("bundle")
		if bundle != "" {
			if err := os.Chdir(bundle); err != nil {
				return err
			}
		}
		if err := checkNoFile(specConfig); err != nil {
			return err
		}
		data, err := json.MarshalIndent(&spec, "", "\t")
		if err != nil {
			return err
		}
		if err := ioutil.WriteFile(specConfig, data, 0666); err != nil {
			return err
		}
		return nil
	},
}

func sPtr(s string) *string      { return &s }
func rPtr(r rune) *rune          { return &r }
func iPtr(i int64) *int64        { return &i }
func u32Ptr(i int64) *uint32     { u := uint32(i); return &u }
func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm }

// loadSpec loads the specification from the provided path.
func loadSpec(cPath string) (spec *specs.Spec, err error) {
	cf, err := os.Open(cPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("JSON specification file %s not found", cPath)
		}
		return nil, err
	}
	defer cf.Close()

	if err = json.NewDecoder(cf).Decode(&spec); err != nil {
		return nil, err
	}
	return spec, validateProcessSpec(&spec.Process)
}

func createLibContainerRlimit(rlimit specs.Rlimit) (configs.Rlimit, error) {
	rl, err := strToRlimit(rlimit.Type)
	if err != nil {
		return configs.Rlimit{}, err
	}
	return configs.Rlimit{
		Type: rl,
		Hard: uint64(rlimit.Hard),
		Soft: uint64(rlimit.Soft),
	}, nil
}
<|endoftext|>


<commit_before>package honeybadger

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime"
	"strings"
)

type Notifier struct {
	Name     string `json:"name"`
	Url      string `json:"url"`
	Version  string `json:"version"`
	Language string `json:"language"`
}

type BacktraceLine struct {
	Method string `json:"method"`
	File   string `json:"file"`
	Number string `json:"number"`
}

type Error struct {
	Class     string                 `json:"class"`
	Message   string                 `json:"message"`
	Backtrace []*BacktraceLine       `json:"backtrace"`
	Source    map[string]interface{} `json:"source"`
}

type Request struct {
	Url       string                 `json:"url"`
	Component string                 `json:"component"`
	Action    string                 `json:"action"`
	Params    map[string]interface{} `json:"params"`
	Session   map[string]interface{} `json:"session"`
	CgiData   map[string]interface{} `json:"cgi_data"`
	Context   map[string]interface{} `json:"context"`
}

type Server struct {
	ProjectRoot     map[string]interface{} `json:"project_root"`
	EnvironmentName string                 `json:"environment_name"`
	Hostname        string                 `json:"hostname"`
}

type Report struct {
	Notifier *Notifier `json:"notifier"`
	Error    *Error    `json:"error"`
	Request  *Request  `json:"request"`
	Server   *Server   `json:"server"`
}

type errBundle struct {
	err error
	context map[string]interface{}
}

var (
	ApiKey string
	Environment string
	errCh chan errBundle
)

func Start(apiKey, environment string) {
	ApiKey = apiKey
	Environment = environment

	if ApiKey != "" {
		const numWorkers = 10
		const bufferSize = 1024
		errCh = make(chan errBundle, bufferSize)
		for i := 0; i < numWorkers; i++{
			go consume()
		}
	}
}

func consume() {
	for b := range errCh {
		report, e := NewReport(b.err)
		if e != nil {
			log.Printf("honeybadger: could not create report: %s", e)
			continue
		}
		if b.context != nil {
			for k,v := range b.context {
				report.AddContext(k,v)
			}
		}
		if e := report.Send(); e != nil {
			log.Printf("honeybadger: could not send report: %s", e)
		}
	}
}

// Send sends a report to HB synchronously and returns the error
func Send(err error) error {
	if ApiKey == "" {
		return err
	}
	report, e := NewReport(err)
	if e != nil {
		log.Printf("honeybadger: could not create report: %s", e)
		return err
	}
	if e := report.Send(); e != nil {
		log.Printf("honeybadger: could not send report: %s", e)
	}
	return err
}

// Dispatch sends a report to HB asynchronously and returns the error
func Dispatch(err error) error {
	return DispatchWithContext(err, nil)
}

// DispatchWithContext sends a report to HB asynchronously with context and
// returns the error
func DispatchWithContext(err error, context map[string]interface{}) error {
	if ApiKey == "" {
		return err
	}
	select {
	case errCh <- errBundle{err:err, context:context}:
	default:
		log.Printf("honeybadger: queue is full, dropping on the floor: %s", err)
	}
	return err
}

// Create a new report using the given error message and current call stack.
func NewReport(msg interface{}) (r *Report, err error) {
	return NewReportWithSkipCallers(msg, 0)
}

// Create a new report using the given error message and current call stack.
// Supply an integer indicating how many callers to skip (0 is none).
func NewReportWithSkipCallers(msg interface{}, skipCallers int) (r *Report, err error) {
	var cwd, hostname string

	if ApiKey == "" {
		err = errors.New("You must set an API key first")
		return
	}

	if Environment == "" {
		err = errors.New("You must set an environment first")
		return
	}

	cwd, _ = os.Getwd()
	hostname, _ = os.Hostname()

	r = &Report{
		Notifier: &Notifier{
			Name:     "Honeybadger (Go)",
			Url:      "https://github.com/jcoene/honeybadger-go",
			Version:  "1.0",
			Language: "Go",
		},
		Error: &Error{
			Class:     "Unknown",
			Message:   fmt.Sprintf("%s", msg),
			Backtrace: make([]*BacktraceLine, 0),
			Source:    make(map[string]interface{}),
		},
		Request: &Request{
			Params:  make(map[string]interface{}),
			Session: make(map[string]interface{}),
			CgiData: make(map[string]interface{}),
			Context: make(map[string]interface{}),
		},
		Server: &Server{
			ProjectRoot:     make(map[string]interface{}),
			EnvironmentName: Environment,
			Hostname:        hostname,
		},
	}

	callers := make([]uintptr, 10)
	runtime.Callers(0, callers)

	for i, pc := range callers {
		if i < 3 { // skip self-inflicted depth
			continue
		}

		fc := runtime.FuncForPC(pc)
		if fc != nil {
			ms := strings.Split(fc.Name(), "/")
			m := ms[len(ms)-1]
			f, n := fc.FileLine(pc)

			// If we reach our origin depth, use the given function name as error class
			if i == skipCallers+2 {
				r.Error.Class = m
			}

			r.Error.Backtrace = append(r.Error.Backtrace, &BacktraceLine{
				Method: m,
				File:   f,
				Number: fmt.Sprintf("%d", n),
			})
		}
	}

	r.Server.ProjectRoot["path"] = cwd

	return
}

// Add a key and given value to the report as context
func (r *Report) AddContext(k string, v interface{}) {
	r.Request.Context[k] = v
}

// Add a key and given value to the report as parameters
func (r *Report) AddParam(k string, v interface{}) {
	r.Request.Params[k] = v
}

// Add a key and given value to the report as session
func (r *Report) AddSession(k string, v interface{}) {
	r.Request.Session[k] = v
}

// Send the report asynchronously
func (r *Report) Dispatch() {
	go func() {
		if err := r.Send(); err != nil {
			log.Printf("honeybadger: could not send report: %s", err)
		}
	}()
}

// Send the report and return an error if present
func (r *Report) Send() (err error) {
	var req *http.Request
	var resp *http.Response
	var payload []byte

	if payload, err = json.MarshalIndent(r, "", "  "); err != nil {
		return
	}

	if req, err = http.NewRequest("POST", "https://api.honeybadger.io/v1/notices", bytes.NewBuffer(payload)); err != nil {
		return
	}

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Accept", "application/json")
	req.Header.Add("X-API-Key", ApiKey)

	if resp, err = http.DefaultClient.Do(req); err != nil {
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode < 201 {
		err = errors.New(fmt.Sprintf("unable to send: error %d", resp.StatusCode))
	}
	return
}
<commit_msg>Output started/not-started on startup<commit_after>package honeybadger

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime"
	"strings"
)

type Notifier struct {
	Name     string `json:"name"`
	Url      string `json:"url"`
	Version  string `json:"version"`
	Language string `json:"language"`
}

type BacktraceLine struct {
	Method string `json:"method"`
	File   string `json:"file"`
	Number string `json:"number"`
}

type Error struct {
	Class     string                 `json:"class"`
	Message   string                 `json:"message"`
	Backtrace []*BacktraceLine       `json:"backtrace"`
	Source    map[string]interface{} `json:"source"`
}

type Request struct {
	Url       string                 `json:"url"`
	Component string                 `json:"component"`
	Action    string                 `json:"action"`
	Params    map[string]interface{} `json:"params"`
	Session   map[string]interface{} `json:"session"`
	CgiData   map[string]interface{} `json:"cgi_data"`
	Context   map[string]interface{} `json:"context"`
}

type Server struct {
	ProjectRoot     map[string]interface{} `json:"project_root"`
	EnvironmentName string                 `json:"environment_name"`
	Hostname        string                 `json:"hostname"`
}

type Report struct {
	Notifier *Notifier `json:"notifier"`
	Error    *Error    `json:"error"`
	Request  *Request  `json:"request"`
	Server   *Server   `json:"server"`
}

type errBundle struct {
	err error
	context map[string]interface{}
}

var (
	ApiKey string
	Environment string
	errCh chan errBundle
)

func Start(apiKey, environment string) {
	ApiKey = apiKey
	Environment = environment

	if ApiKey == "" {
		log.Println("Honeybadger disabled")
		return
	}

	const numWorkers = 10
	const bufferSize = 1024
	errCh = make(chan errBundle, bufferSize)
	for i := 0; i < numWorkers; i++{
		go consume()
	}
	log.Println("Honeybadger client started")
}

func consume() {
	for b := range errCh {
		report, e := NewReport(b.err)
		if e != nil {
			log.Printf("honeybadger: could not create report: %s", e)
			continue
		}
		if b.context != nil {
			for k,v := range b.context {
				report.AddContext(k,v)
			}
		}
		if e := report.Send(); e != nil {
			log.Printf("honeybadger: could not send report: %s", e)
		}
	}
}

// Send sends a report to HB synchronously and returns the error
func Send(err error) error {
	if ApiKey == "" {
		return err
	}
	report, e := NewReport(err)
	if e != nil {
		log.Printf("honeybadger: could not create report: %s", e)
		return err
	}
	if e := report.Send(); e != nil {
		log.Printf("honeybadger: could not send report: %s", e)
	}
	return err
}

// Dispatch sends a report to HB asynchronously and returns the error
func Dispatch(err error) error {
	return DispatchWithContext(err, nil)
}

// DispatchWithContext sends a report to HB asynchronously with context and
// returns the error
func DispatchWithContext(err error, context map[string]interface{}) error {
	if ApiKey == "" {
		return err
	}
	select {
	case errCh <- errBundle{err:err, context:context}:
	default:
		log.Printf("honeybadger: queue is full, dropping on the floor: %s", err)
	}
	return err
}

// Create a new report using the given error message and current call stack.
func NewReport(msg interface{}) (r *Report, err error) {
	return NewReportWithSkipCallers(msg, 0)
}

// Create a new report using the given error message and current call stack.
// Supply an integer indicating how many callers to skip (0 is none).
func NewReportWithSkipCallers(msg interface{}, skipCallers int) (r *Report, err error) {
	var cwd, hostname string

	if ApiKey == "" {
		err = errors.New("You must set an API key first")
		return
	}

	if Environment == "" {
		err = errors.New("You must set an environment first")
		return
	}

	cwd, _ = os.Getwd()
	hostname, _ = os.Hostname()

	r = &Report{
		Notifier: &Notifier{
			Name:     "Honeybadger (Go)",
			Url:      "https://github.com/jcoene/honeybadger-go",
			Version:  "1.0",
			Language: "Go",
		},
		Error: &Error{
			Class:     "Unknown",
			Message:   fmt.Sprintf("%s", msg),
			Backtrace: make([]*BacktraceLine, 0),
			Source:    make(map[string]interface{}),
		},
		Request: &Request{
			Params:  make(map[string]interface{}),
			Session: make(map[string]interface{}),
			CgiData: make(map[string]interface{}),
			Context: make(map[string]interface{}),
		},
		Server: &Server{
			ProjectRoot:     make(map[string]interface{}),
			EnvironmentName: Environment,
			Hostname:        hostname,
		},
	}

	callers := make([]uintptr, 10)
	runtime.Callers(0, callers)

	for i, pc := range callers {
		if i < 3 { // skip self-inflicted depth
			continue
		}

		fc := runtime.FuncForPC(pc)
		if fc != nil {
			ms := strings.Split(fc.Name(), "/")
			m := ms[len(ms)-1]
			f, n := fc.FileLine(pc)

			// If we reach our origin depth, use the given function name as error class
			if i == skipCallers+2 {
				r.Error.Class = m
			}

			r.Error.Backtrace = append(r.Error.Backtrace, &BacktraceLine{
				Method: m,
				File:   f,
				Number: fmt.Sprintf("%d", n),
			})
		}
	}

	r.Server.ProjectRoot["path"] = cwd

	return
}

// Add a key and given value to the report as context
func (r *Report) AddContext(k string, v interface{}) {
	r.Request.Context[k] = v
}

// Add a key and given value to the report as parameters
func (r *Report) AddParam(k string, v interface{}) {
	r.Request.Params[k] = v
}

// Add a key and given value to the report as session
func (r *Report) AddSession(k string, v interface{}) {
	r.Request.Session[k] = v
}

// Send the report asynchronously
func (r *Report) Dispatch() {
	go func() {
		if err := r.Send(); err != nil {
			log.Printf("honeybadger: could not send report: %s", err)
		}
	}()
}

// Send the report and return an error if present
func (r *Report) Send() (err error) {
	var req *http.Request
	var resp *http.Response
	var payload []byte

	if payload, err = json.MarshalIndent(r, "", "  "); err != nil {
		return
	}

	if req, err = http.NewRequest("POST", "https://api.honeybadger.io/v1/notices", bytes.NewBuffer(payload)); err != nil {
		return
	}

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Accept", "application/json")
	req.Header.Add("X-API-Key", ApiKey)

	if resp, err = http.DefaultClient.Do(req); err != nil {
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode < 201 {
		err = errors.New(fmt.Sprintf("unable to send: error %d", resp.StatusCode))
	}
	return
}
<|endoftext|>


<commit_before>package restorer

import (
	"bytes"
	"context"
	"io"
	"io/ioutil"
	"testing"

	"github.com/restic/restic/internal/crypto"
	"github.com/restic/restic/internal/restic"
	rtest "github.com/restic/restic/internal/test"
)

type TestBlob struct {
	data string
	pack string
}

type TestFile struct {
	name  string
	blobs []TestBlob
}

type TestRepo struct {
	key *crypto.Key

	// pack names and ids
	packsNameToID map[string]restic.ID
	packsIDToName map[restic.ID]string
	packsIDToData map[restic.ID][]byte

	// blobs and files
	blobs              map[restic.ID][]restic.PackedBlob
	files              []*fileInfo
	filesPathToContent map[string]string

	//
	loader func(ctx context.Context, h restic.Handle, length int, offset int64, fn func(rd io.Reader) error) error
}

func (i *TestRepo) Lookup(bh restic.BlobHandle) []restic.PackedBlob {
	packs := i.blobs[bh.ID]
	return packs
}

func (i *TestRepo) packName(pack *packInfo) string {
	return i.packsIDToName[pack.id]
}

func (i *TestRepo) packID(name string) restic.ID {
	return i.packsNameToID[name]
}

func (i *TestRepo) fileContent(file *fileInfo) string {
	return i.filesPathToContent[file.location]
}

func newTestRepo(content []TestFile) *TestRepo {
	type Pack struct {
		name  string
		data  []byte
		blobs map[restic.ID]restic.Blob
	}
	packs := make(map[string]Pack)

	key := crypto.NewRandomKey()
	seal := func(data []byte) []byte {
		ciphertext := restic.NewBlobBuffer(len(data))
		ciphertext = ciphertext[:0] // truncate the slice
		nonce := crypto.NewRandomNonce()
		ciphertext = append(ciphertext, nonce...)
		return key.Seal(ciphertext, nonce, data, nil)
	}

	filesPathToContent := make(map[string]string)

	for _, file := range content {
		var content string
		for _, blob := range file.blobs {
			content += blob.data

			// get the pack, create as necessary
			var pack Pack
			var found bool
			if pack, found = packs[blob.pack]; !found {
				pack = Pack{name: blob.pack, blobs: make(map[restic.ID]restic.Blob)}
			}

			// calculate blob id and add to the pack as necessary
			blobID := restic.Hash([]byte(blob.data))
			if _, found := pack.blobs[blobID]; !found {
				blobData := seal([]byte(blob.data))
				pack.blobs[blobID] = restic.Blob{
					BlobHandle: restic.BlobHandle{
						Type: restic.DataBlob,
						ID:   blobID,
					},
					Length: uint(len(blobData)),
					Offset: uint(len(pack.data)),
				}
				pack.data = append(pack.data, blobData...)
			}

			packs[blob.pack] = pack
		}
		filesPathToContent[file.name] = content
	}

	blobs := make(map[restic.ID][]restic.PackedBlob)
	packsIDToName := make(map[restic.ID]string)
	packsIDToData := make(map[restic.ID][]byte)
	packsNameToID := make(map[string]restic.ID)

	for _, pack := range packs {
		packID := restic.Hash(pack.data)
		packsIDToName[packID] = pack.name
		packsIDToData[packID] = pack.data
		packsNameToID[pack.name] = packID
		for blobID, blob := range pack.blobs {
			blobs[blobID] = append(blobs[blobID], restic.PackedBlob{Blob: blob, PackID: packID})
		}
	}

	var files []*fileInfo
	for _, file := range content {
		content := restic.IDs{}
		for _, blob := range file.blobs {
			content = append(content, restic.Hash([]byte(blob.data)))
		}
		files = append(files, &fileInfo{location: file.name, blobs: content})
	}

	repo := &TestRepo{
		key:                key,
		packsIDToName:      packsIDToName,
		packsIDToData:      packsIDToData,
		packsNameToID:      packsNameToID,
		blobs:              blobs,
		files:              files,
		filesPathToContent: filesPathToContent,
	}
	repo.loader = func(ctx context.Context, h restic.Handle, length int, offset int64, fn func(rd io.Reader) error) error {
		packID, err := restic.ParseID(h.Name)
		if err != nil {
			return err
		}
		rd := bytes.NewReader(repo.packsIDToData[packID][int(offset) : int(offset)+length])
		return fn(rd)
	}

	return repo
}

func restoreAndVerify(t *testing.T, tempdir string, content []TestFile) {
	repo := newTestRepo(content)

	r := newFileRestorer(tempdir, repo.loader, repo.key, repo.Lookup)
	r.files = repo.files

	err := r.restoreFiles(context.TODO())
	rtest.OK(t, err)

	for _, file := range repo.files {
		target := r.targetPath(file.location)
		data, err := ioutil.ReadFile(target)
		if err != nil {
			t.Errorf("unable to read file %v: %v", file.location, err)
			continue
		}

		content := repo.fileContent(file)
		if !bytes.Equal(data, []byte(content)) {
			t.Errorf("file %v has wrong content: want %q, got %q", file.location, content, data)
		}
	}
}

func TestFileRestorerBasic(t *testing.T) {
	tempdir, cleanup := rtest.TempDir(t)
	defer cleanup()

	restoreAndVerify(t, tempdir, []TestFile{
		{
			name: "file1",
			blobs: []TestBlob{
				{"data1-1", "pack1-1"},
				{"data1-2", "pack1-2"},
			},
		},
		{
			name: "file2",
			blobs: []TestBlob{
				{"data2-1", "pack2-1"},
				{"data2-2", "pack2-2"},
			},
		},
		{
			name: "file3",
			blobs: []TestBlob{
				// same blob multiple times
				{"data3-1", "pack3-1"},
				{"data3-1", "pack3-1"},
			},
		},
	})
}
<commit_msg>Add filerestorer test where only parts of pack are used<commit_after>package restorer

import (
	"bytes"
	"context"
	"io"
	"io/ioutil"
	"testing"

	"github.com/restic/restic/internal/crypto"
	"github.com/restic/restic/internal/restic"
	rtest "github.com/restic/restic/internal/test"
)

type TestBlob struct {
	data string
	pack string
}

type TestFile struct {
	name  string
	blobs []TestBlob
}

type TestRepo struct {
	key *crypto.Key

	// pack names and ids
	packsNameToID map[string]restic.ID
	packsIDToName map[restic.ID]string
	packsIDToData map[restic.ID][]byte

	// blobs and files
	blobs              map[restic.ID][]restic.PackedBlob
	files              []*fileInfo
	filesPathToContent map[string]string

	//
	loader func(ctx context.Context, h restic.Handle, length int, offset int64, fn func(rd io.Reader) error) error
}

func (i *TestRepo) Lookup(bh restic.BlobHandle) []restic.PackedBlob {
	packs := i.blobs[bh.ID]
	return packs
}

func (i *TestRepo) packName(pack *packInfo) string {
	return i.packsIDToName[pack.id]
}

func (i *TestRepo) packID(name string) restic.ID {
	return i.packsNameToID[name]
}

func (i *TestRepo) fileContent(file *fileInfo) string {
	return i.filesPathToContent[file.location]
}

func newTestRepo(content []TestFile) *TestRepo {
	type Pack struct {
		name  string
		data  []byte
		blobs map[restic.ID]restic.Blob
	}
	packs := make(map[string]Pack)

	key := crypto.NewRandomKey()
	seal := func(data []byte) []byte {
		ciphertext := restic.NewBlobBuffer(len(data))
		ciphertext = ciphertext[:0] // truncate the slice
		nonce := crypto.NewRandomNonce()
		ciphertext = append(ciphertext, nonce...)
		return key.Seal(ciphertext, nonce, data, nil)
	}

	filesPathToContent := make(map[string]string)

	for _, file := range content {
		var content string
		for _, blob := range file.blobs {
			content += blob.data

			// get the pack, create as necessary
			var pack Pack
			var found bool
			if pack, found = packs[blob.pack]; !found {
				pack = Pack{name: blob.pack, blobs: make(map[restic.ID]restic.Blob)}
			}

			// calculate blob id and add to the pack as necessary
			blobID := restic.Hash([]byte(blob.data))
			if _, found := pack.blobs[blobID]; !found {
				blobData := seal([]byte(blob.data))
				pack.blobs[blobID] = restic.Blob{
					BlobHandle: restic.BlobHandle{
						Type: restic.DataBlob,
						ID:   blobID,
					},
					Length: uint(len(blobData)),
					Offset: uint(len(pack.data)),
				}
				pack.data = append(pack.data, blobData...)
			}

			packs[blob.pack] = pack
		}
		filesPathToContent[file.name] = content
	}

	blobs := make(map[restic.ID][]restic.PackedBlob)
	packsIDToName := make(map[restic.ID]string)
	packsIDToData := make(map[restic.ID][]byte)
	packsNameToID := make(map[string]restic.ID)

	for _, pack := range packs {
		packID := restic.Hash(pack.data)
		packsIDToName[packID] = pack.name
		packsIDToData[packID] = pack.data
		packsNameToID[pack.name] = packID
		for blobID, blob := range pack.blobs {
			blobs[blobID] = append(blobs[blobID], restic.PackedBlob{Blob: blob, PackID: packID})
		}
	}

	var files []*fileInfo
	for _, file := range content {
		content := restic.IDs{}
		for _, blob := range file.blobs {
			content = append(content, restic.Hash([]byte(blob.data)))
		}
		files = append(files, &fileInfo{location: file.name, blobs: content})
	}

	repo := &TestRepo{
		key:                key,
		packsIDToName:      packsIDToName,
		packsIDToData:      packsIDToData,
		packsNameToID:      packsNameToID,
		blobs:              blobs,
		files:              files,
		filesPathToContent: filesPathToContent,
	}
	repo.loader = func(ctx context.Context, h restic.Handle, length int, offset int64, fn func(rd io.Reader) error) error {
		packID, err := restic.ParseID(h.Name)
		if err != nil {
			return err
		}
		rd := bytes.NewReader(repo.packsIDToData[packID][int(offset) : int(offset)+length])
		return fn(rd)
	}

	return repo
}

func restoreAndVerify(t *testing.T, tempdir string, content []TestFile, files map[string]bool) {
	repo := newTestRepo(content)

	r := newFileRestorer(tempdir, repo.loader, repo.key, repo.Lookup)

	if files == nil {
		r.files = repo.files
	} else {
		for _, file := range repo.files {
			if files[file.location] {
				r.files = append(r.files, file)
			}
		}
	}

	err := r.restoreFiles(context.TODO())
	rtest.OK(t, err)

	for _, file := range r.files {
		target := r.targetPath(file.location)
		data, err := ioutil.ReadFile(target)
		if err != nil {
			t.Errorf("unable to read file %v: %v", file.location, err)
			continue
		}

		content := repo.fileContent(file)
		if !bytes.Equal(data, []byte(content)) {
			t.Errorf("file %v has wrong content: want %q, got %q", file.location, content, data)
		}
	}
}

func TestFileRestorerBasic(t *testing.T) {
	tempdir, cleanup := rtest.TempDir(t)
	defer cleanup()

	restoreAndVerify(t, tempdir, []TestFile{
		{
			name: "file1",
			blobs: []TestBlob{
				{"data1-1", "pack1-1"},
				{"data1-2", "pack1-2"},
			},
		},
		{
			name: "file2",
			blobs: []TestBlob{
				{"data2-1", "pack2-1"},
				{"data2-2", "pack2-2"},
			},
		},
		{
			name: "file3",
			blobs: []TestBlob{
				// same blob multiple times
				{"data3-1", "pack3-1"},
				{"data3-1", "pack3-1"},
			},
		},
	}, nil)
}

func TestFileRestorerPackSkip(t *testing.T) {
	tempdir, cleanup := rtest.TempDir(t)
	defer cleanup()

	files := make(map[string]bool)
	files["file2"] = true

	restoreAndVerify(t, tempdir, []TestFile{
		{
			name: "file1",
			blobs: []TestBlob{
				{"data1-1", "pack1"},
				{"data1-2", "pack1"},
				{"data1-3", "pack1"},
				{"data1-4", "pack1"},
				{"data1-5", "pack1"},
				{"data1-6", "pack1"},
			},
		},
		{
			name: "file2",
			blobs: []TestBlob{
				// file is contained in pack1 but need pack parts to be skipped
				{"data1-2", "pack1"},
				{"data1-4", "pack1"},
				{"data1-6", "pack1"},
			},
		},
	}, files)
}
<|endoftext|>


<commit_before>package http

import (
	"fmt"
	"github.com/pierrre/imageserver"
	"net/http"
	"regexp"
	"strconv"
	"time"
)

var inmHeaderRegexp, _ = regexp.Compile("^\"(.+)\"$")

var expiresHeaderLocation, _ = time.LoadLocation("GMT")

type Server struct {
	HttpServer  *http.Server
	Parser      Parser
	ImageServer *imageserver.Server

	Expire time.Duration
}

func (server *Server) Serve() {
	serveMux := http.NewServeMux()
	serveMux.HandleFunc("/", server.handleHttpRequest)
	server.HttpServer.Handler = serveMux
	server.HttpServer.ListenAndServe()
}

func (server *Server) handleHttpRequest(writer http.ResponseWriter, request *http.Request) {
	if request.Method != "GET" {
		server.sendError(writer, fmt.Errorf("Invalid request method"))
		return
	}

	parameters := make(imageserver.Parameters)
	err := server.Parser.Parse(request, parameters)
	if err != nil {
		server.sendError(writer, err)
		return
	}

	if server.checkNotModified(writer, request, parameters) {
		return
	}

	image, err := server.ImageServer.Get(parameters)
	if err != nil {
		server.sendError(writer, err)
		return
	}

	server.sendImage(writer, image, parameters)
}

func (server *Server) checkNotModified(writer http.ResponseWriter, request *http.Request, parameters imageserver.Parameters) bool {
	inmHeader := request.Header.Get("If-None-Match")
	if len(inmHeader) > 0 {
		matches := inmHeaderRegexp.FindStringSubmatch(inmHeader)
		if matches != nil && len(matches) == 2 {
			inm := matches[1]
			if inm == parameters.Hash() {
				writer.WriteHeader(http.StatusNotModified)
				return true
			}
		}
	}
	return false
}

func (server *Server) sendImage(writer http.ResponseWriter, image *imageserver.Image, parameters imageserver.Parameters) {
	writer.Header().Set("Content-Length", strconv.Itoa(len(image.Data)))

	if len(image.Type) > 0 {
		writer.Header().Set("Content-Type", "image/"+image.Type)
	}

	writer.Header().Set("Cache-Control", "public")

	if server.Expire != 0 {
		t := time.Now()
		t = t.Add(server.Expire)
		t = t.In(expiresHeaderLocation)
		writer.Header().Set("Expires", t.Format(time.RFC1123))
	}

	writer.Header().Set("ETag", fmt.Sprintf("\"%s\"", parameters.Hash()))

	writer.Write(image.Data)
}

func (server *Server) sendError(writer http.ResponseWriter, err error) {
	http.Error(writer, err.Error(), http.StatusBadRequest)
}
<commit_msg>Add http _ping route<commit_after>package http

import (
	"fmt"
	"github.com/pierrre/imageserver"
	"net/http"
	"regexp"
	"strconv"
	"time"
)

var inmHeaderRegexp, _ = regexp.Compile("^\"(.+)\"$")

var expiresHeaderLocation, _ = time.LoadLocation("GMT")

type Server struct {
	HttpServer  *http.Server
	Parser      Parser
	ImageServer *imageserver.Server

	Expire time.Duration
}

func (server *Server) Serve() {
	serveMux := http.NewServeMux()
	serveMux.HandleFunc("/", server.handleHttpRequest)
	serveMux.HandleFunc("/_ping", server.handleHttpRequestPing)
	server.HttpServer.Handler = serveMux
	server.HttpServer.ListenAndServe()
}

func (server *Server) handleHttpRequest(writer http.ResponseWriter, request *http.Request) {
	if request.Method != "GET" {
		server.sendError(writer, fmt.Errorf("Invalid request method"))
		return
	}

	parameters := make(imageserver.Parameters)
	err := server.Parser.Parse(request, parameters)
	if err != nil {
		server.sendError(writer, err)
		return
	}

	if server.checkNotModified(writer, request, parameters) {
		return
	}

	image, err := server.ImageServer.Get(parameters)
	if err != nil {
		server.sendError(writer, err)
		return
	}

	server.sendImage(writer, image, parameters)
}

func (server *Server) checkNotModified(writer http.ResponseWriter, request *http.Request, parameters imageserver.Parameters) bool {
	inmHeader := request.Header.Get("If-None-Match")
	if len(inmHeader) > 0 {
		matches := inmHeaderRegexp.FindStringSubmatch(inmHeader)
		if matches != nil && len(matches) == 2 {
			inm := matches[1]
			if inm == parameters.Hash() {
				writer.WriteHeader(http.StatusNotModified)
				return true
			}
		}
	}
	return false
}

func (server *Server) sendImage(writer http.ResponseWriter, image *imageserver.Image, parameters imageserver.Parameters) {
	writer.Header().Set("Content-Length", strconv.Itoa(len(image.Data)))

	if len(image.Type) > 0 {
		writer.Header().Set("Content-Type", "image/"+image.Type)
	}

	writer.Header().Set("Cache-Control", "public")

	if server.Expire != 0 {
		t := time.Now()
		t = t.Add(server.Expire)
		t = t.In(expiresHeaderLocation)
		writer.Header().Set("Expires", t.Format(time.RFC1123))
	}

	writer.Header().Set("ETag", fmt.Sprintf("\"%s\"", parameters.Hash()))

	writer.Write(image.Data)
}

func (server *Server) sendError(writer http.ResponseWriter, err error) {
	http.Error(writer, err.Error(), http.StatusBadRequest)
}

func (server *Server) handleHttpRequestPing(writer http.ResponseWriter, request *http.Request) {
	writer.WriteHeader(200)
}
<|endoftext|>


<commit_before>package main

import (
	"math/rand"
	"os"
	"io/ioutil"
	"time"
	"github.com/nsf/termbox-go"
)

// SETUP
const WAIT = 50
const ALLOW_BACK_OVER_LINE = false
const START_CHAR = 'S'
const FINISH_CHAR = 'F'
const DEFAULT_TRACK = "t1.tr"
const FINISH_LINE = '|'

// Board
var board []rune
var tempBoard = make([]rune, len(board))

// Player
type Player struct {
	Symbol	rune
	X 		int
	Y		int
	laps 	int//= 0
	flFlag  bool//= false
	frFlag 	bool//= false
}
var pl1 =	Player{2015, 3, 3, 0, false, false}
var pl2 = Player{2016, 3, 3, 0, false, false}
var players = []Player{pl1, pl2}

// Channels
var channels []chan int
var globalChangeChanel chan int

// Controler
func NewControler(pl *Player, moveFromInputer chan int) {
	for {
		move := <-moveFromInputer
		pl.moveIfPossible(move)
	}
}

// Inputers
func NewRandomInputer(moveToControler chan int) {
	for {
		wait(WAIT)
		move := getRandomMove()
		moveToControler <- move
	}
}

func NewKeyInputer(moveToControler chan int, moveFromKeyListenerControler chan int) {
	for {
		var move = <- moveFromKeyListenerControler
		moveToControler <- move
	}
}

// Finish Line
type FinishLine struct {
	X 		int
	Y		int
}
var fL []FinishLine
var fR []FinishLine

//type KeyGroup string
type KeyGroup int
const (
   	ARROW_KEYS = 0
	ASWD_KEYS = 1
)

func main() {
	// Termbox init
	termboxErr := termbox.Init()
	if termboxErr != nil {
        panic(termboxErr)
	}
	defer termbox.Close()
	termbox.HideCursor()
	// Track init
	var trackFileName string
	if  len(os.Args) > 1 {
		trackFileName = os.Args[1]
	} else {
		trackFileName = DEFAULT_TRACK
	}
	var boardByte, readfileErr = ioutil.ReadFile(trackFileName)
	if (readfileErr != nil) {
		panic ("Could not open file with track")
	}
	var boardString = string(boardByte)
	board = []rune(boardString)
	tempBoard = make([]rune, len(board))
	getFinishLines()
	drawFinishLine()
	putPlayersOnStart()
	// Draw starting position
	draw(getBoard())
	termbox.Flush()

	// Controlers and imputers
	globalChangeChanel = make(chan int)

	//connectModules()
	// create channels
	for i := 0; i < len(players); i++ {
		var chanel = make(chan int)
		channels = append(channels, chanel)
	}
	// create modules
	go NewControler(&players[0], channels[0])
	go NewRandomInputer(channels[0])
	//go NewControler(&players[1], channels[1])
	//go NewRandomInputer(channels[1])
	var keyListener2KeyInputer = make(chan int)
	go NewControler(&players[1], channels[1])
	go NewKeyInputer(channels[1], keyListener2KeyInputer)

	// Key reader goroutine
	var keyGroup2ChannelMaper = map[KeyGroup] chan int {
		ARROW_KEYS: keyListener2KeyInputer,
	}
	go listenToKeys(keyGroup2ChannelMaper)

	// Main Loop
	for !checkWin() {
		<- globalChangeChanel
		brd := getBoard()
		draw(brd)
		termbox.Flush()
	}
}

/*
func connectModules() {
	// create channels
	for i := 0; i < len(players); i++ {
		var chanel = make(chan int)
		channels = append(channels, chanel)
	}
	// create modules
	go NewControler(&players[0], channels[0])
	go NewRandomInputer(channels[0])

	//go NewControler(&players[1], channels[1])
	//go NewRandomInputer(channels[1])

	var keyListener2KeyInputer = make(chan int)
	go NewControler(&players[1], channels[1])
	go NewKeyInputer(channels[1], keyListener2KeyInputer)

	/*
	for i, ch := range channels {
		go NewControler(&players[i], ch)
		go NewRandomInputer(ch)
	}
	*/
//}

func putPlayersOnStart() {
	i := 0
	for _, pl := range players {
		if len(fR) == i {
			i = 0
		}
		sL := fR[i]
		i++
		pl.X = sL.X
		pl.Y = sL.Y
	}
}

func (pl *Player) moveIfPossible(move int) {
	if (pl.isMoveOk(move)) {
		pl.executeMove(move)
	}
}

func drawFinishLine() {
	// gre cez cev board, zamena
	for key, val := range board {
		if val == START_CHAR {
			board[key] = ' '
		}
		if val == FINISH_CHAR {
			board[key] = FINISH_LINE
		}
	}
}

/*
const (
        _           = iota // ignore first value by assigning to blank identifier
        KB ByteSize = 1 << (10 * iota)
        MB
        GB
        TB
        PB
        EB
        ZB
        YB
)
const KeyGroup = (
	ARROW_KEYS = 0
	ASWD_KEYS = 1
)
*/

//TODO change to key listener module
func listenToKeys(chnls map[KeyGroup]chan int) {
	ev := termbox.PollEvent()
	// When it gets event it recusively calls itself,
	// so it listens for new events as soon as possible.
	go listenToKeys(chnls)
	switch ev.Type {
		case termbox.EventKey:
			if ev.Key == termbox.KeyCtrlC {
				termbox.Close()
				panic("Don't know how else to exit program:(")
			}
			ch, present := chnls[ARROW_KEYS]
			if present {
				if ev.Key == termbox.KeyArrowUp {
					ch <- 1
				}
				if ev.Key == termbox.KeyArrowRight {
					ch <- 2
				}
				if ev.Key == termbox.KeyArrowDown {
					ch <- 3
				}
				if ev.Key == termbox.KeyArrowLeft {
					ch <- 4
				}
			}
		case termbox.EventError:
			panic(ev.Err)
	}
}

func draw(text string) {
	var x=0; var y=0
	for _, value := range text {
		if value == '\n' {
			x=0
			y++
			continue
		}
		termbox.SetCell(x, y, value, 	termbox.ColorWhite, 										termbox.ColorBlack)
		x++
	}
}

func getFinishLines() {
	for key, value := range board {
		if value == START_CHAR {
			var x, y = getPosXY(key)
			fR = append(fR,FinishLine{x,y})
		}
		if value == FINISH_CHAR {
			var x, y = getPosXY(key)
			fL = append(fL,FinishLine{x,y})
		}
	}
}

func (pl *Player) checkCircle() {
	// na liniji
	if doesArrayContainPosition(fL, pl.getPosInt()) {
		if (pl.frFlag == true) {
			pl.laps--
			pl.flFlag = true
			pl.frFlag = false
		} else {
			pl.flFlag = true
		}
	// desno od linije
    } else if doesArrayContainPosition(fR, pl.getPosInt()) {
    	if (pl.flFlag == true) {
			pl.laps++
			pl.frFlag = true
			pl.flFlag = false
		} else {
			pl.frFlag = true
		}
	// drugje
    } else {
		pl.flFlag = false
		pl.frFlag = false
	}
}

func doesArrayContainPosition(fll []FinishLine, position int) bool {
	for _, fl := range fll {
		if position == fl.getPosInt() {
			return true
		}
	}
	return false
}

func toString(pl Player) string {
	return string(pl.Symbol) +" "+ string(pl.X) +" "+ string(pl.Y)
}

func getBoard() string {
	copy(tempBoard[:], board[0:len(board)])
	for _, pl := range players {
		pl.drawPlayerOnBoard(tempBoard)
	}
	return string(tempBoard)
}

func (pl *Player) drawPlayerOnBoard(boardLoc []rune) {
	var posInt = pl.getPosInt()
	boardLoc[posInt] = pl.Symbol
}

func getPosXY(pos int) (int, int) {
	var x = 0
	var y = 0
	for i := 0; i<=pos; i++ {
		x++
		if board[i] == '\n' {
			y++
			x = 0
		}
	}
	return x, y
}

func getPosInt(x int, y int) int {
	for i := 0; i<len(board); i++ {
		if board[i] == '\n' {
			y--
		}
		if y == 0 {
			x--
		}
		if y == 0 && x == 0 {
			return i
		}
	}
	panic("getPosInt: wrong coordinates")
}

func (pl *Player) getPosInt() int {
	return getPosInt(pl.X, pl.Y)
}

func (fl *FinishLine) getPosInt() int {
	return getPosInt(fl.X, fl.Y)
}

func (pl *Player) isMoveOk(move int) bool {
	var newPos = getPos(pl.X, pl.Y, move)
	var sym = board[newPos]
	if contains([]rune{' ', '|'}, sym) {
		if !ALLOW_BACK_OVER_LINE {
			return !headingBackOverLine(pl.getPosInt(), newPos)
		} else {
			return true
		}
	}
	return false
}

//TODO da ne gre niti na crto
func headingBackOverLine(oldPos int, newPos int) bool {
	// v naslednji potezi bi na ciljni crti
	// in zdaj je desno od ciljne crte
	if 	doesArrayContainPosition(fL, newPos) &&
		doesArrayContainPosition(fR, oldPos) {
		return true
	}
    return false
}

func contains(arr []rune, sim rune) bool {
	for _, val := range arr {
		if val == sim {
			return true
		}
	}
	return false
}

func (pl *Player) executeMove(move int) {
	if move == 1 {
		pl.Y--
	}
	if move == 2 {
		pl.X++
	}
	if move == 3 {
		pl.Y++
	}
	if move == 4 {
		pl.X--
	}
	pl.checkCircle()
	globalChangeChanel <- 1
}

func getPos(x int, y int, move int) int {
	if move == 1 {
		return getPosInt(x, y-1)
	}
	if move == 2 {
		return getPosInt(x+1, y)
	}
	if move == 3 {
		return getPosInt(x, y+1)
	}
	if move == 4 {
		return getPosInt(x-1, y)
	}
	panic("Invalid move value!")
}

//1:up 2:right 3:down 4: left
func getRandomMove() int {
    var rt = rand.Perm(4)
	var rrt = rt[1]+1
	return rrt
}

func wait(sec int) {
	var ssec = time.Duration(sec*1000000)
	time.Sleep(ssec)
}

func checkWin() bool{
   return false
}

<commit_msg>cleanup<commit_after>package main

import (
	"math/rand"
	"os"
	"io/ioutil"
	"time"
	"github.com/nsf/termbox-go"
)

// SETUP
const WAIT = 50
const ALLOW_BACK_OVER_LINE = false
const START_CHAR = 'S'
const FINISH_CHAR = 'F'
const DEFAULT_TRACK = "t1.tr"
const FINISH_LINE = '|'

// Board
var board []rune
var tempBoard = make([]rune, len(board))

// Player
type Player struct {
	Symbol	rune
	X 		int
	Y		int
	laps 	int//= 0
	flFlag  bool//= false
	frFlag 	bool//= false
}
var pl1 =	Player{2015, 3, 3, 0, false, false}
var pl2 = Player{2016, 3, 3, 0, false, false}
var players = []Player{pl1, pl2}

// Channels
var channels []chan int
var globalChangeChanel chan int

// Controler
func NewControler(pl *Player, moveFromInputer chan int) {
	for {
		move := <-moveFromInputer
		pl.moveIfPossible(move)
	}
}

// Inputers
func NewRandomInputer(moveToControler chan int) {
	for {
		wait(WAIT)
		move := getRandomMove()
		moveToControler <- move
	}
}

func NewKeyInputer(moveToControler chan int, moveFromKeyListenerControler chan int) {
	for {
		var move = <- moveFromKeyListenerControler
		moveToControler <- move
	}
}

// Finish Line
type FinishLine struct {
	X 		int
	Y		int
}
var fL []FinishLine
var fR []FinishLine

//type KeyGroup
type KeyGroup int
const (
   	ARROW_KEYS = 0
	ASWD_KEYS = 1
)

func main() {
	// Termbox init
	termboxErr := termbox.Init()
	if termboxErr != nil {
        panic(termboxErr)
	}
	defer termbox.Close()
	termbox.HideCursor()
	// Track init
	var trackFileName string
	if  len(os.Args) > 1 {
		trackFileName = os.Args[1]
	} else {
		trackFileName = DEFAULT_TRACK
	}
	var boardByte, readfileErr = ioutil.ReadFile(trackFileName)
	if (readfileErr != nil) {
		panic ("Could not open file with track")
	}
	var boardString = string(boardByte)
	board = []rune(boardString)
	tempBoard = make([]rune, len(board))
	getFinishLines()
	drawFinishLine()
	putPlayersOnStart()
	// Draw starting position
	draw(getBoard())
	termbox.Flush()

	// Controlers and imputers
	globalChangeChanel = make(chan int)

	//connectModules()
	// create channels
	for i := 0; i < len(players); i++ {
		var chanel = make(chan int)
		channels = append(channels, chanel)
	}
	// create modules
	go NewControler(&players[0], channels[0])
	go NewRandomInputer(channels[0])
	//go NewControler(&players[1], channels[1])
	//go NewRandomInputer(channels[1])
	var keyListener2KeyInputer = make(chan int)
	go NewControler(&players[1], channels[1])
	go NewKeyInputer(channels[1], keyListener2KeyInputer)

	// Key reader goroutine
	var keyGroup2ChannelMaper = map[KeyGroup] chan int {
		ARROW_KEYS: keyListener2KeyInputer,
	}
	go listenToKeys(keyGroup2ChannelMaper)

	// Main Loop
	for !checkWin() {
		<- globalChangeChanel
		brd := getBoard()
		draw(brd)
		termbox.Flush()
	}
}

func putPlayersOnStart() {
	i := 0
	for _, pl := range players {
		if len(fR) == i {
			i = 0
		}
		sL := fR[i]
		i++
		pl.X = sL.X
		pl.Y = sL.Y
	}
}

func (pl *Player) moveIfPossible(move int) {
	if (pl.isMoveOk(move)) {
		pl.executeMove(move)
	}
}

func drawFinishLine() {
	// gre cez cev board, zamena
	for key, val := range board {
		if val == START_CHAR {
			board[key] = ' '
		}
		if val == FINISH_CHAR {
			board[key] = FINISH_LINE
		}
	}
}

func listenToKeys(chnls map[KeyGroup]chan int) {
	ev := termbox.PollEvent()
	// When it gets event it recusively calls itself,
	// so it listens for new events as soon as possible.
	go listenToKeys(chnls)
	switch ev.Type {
		case termbox.EventKey:
			if ev.Key == termbox.KeyCtrlC {
				termbox.Close()
				panic("Don't know how else to exit program:(")
			}
			ch, present := chnls[ARROW_KEYS]
			if present {
				if ev.Key == termbox.KeyArrowUp {
					ch <- 1
				}
				if ev.Key == termbox.KeyArrowRight {
					ch <- 2
				}
				if ev.Key == termbox.KeyArrowDown {
					ch <- 3
				}
				if ev.Key == termbox.KeyArrowLeft {
					ch <- 4
				}
			}
		case termbox.EventError:
			panic(ev.Err)
	}
}

func draw(text string) {
	var x=0; var y=0
	for _, value := range text {
		if value == '\n' {
			x=0
			y++
			continue
		}
		termbox.SetCell(x, y, value, 	termbox.ColorWhite, 										termbox.ColorBlack)
		x++
	}
}

func getFinishLines() {
	for key, value := range board {
		if value == START_CHAR {
			var x, y = getPosXY(key)
			fR = append(fR,FinishLine{x,y})
		}
		if value == FINISH_CHAR {
			var x, y = getPosXY(key)
			fL = append(fL,FinishLine{x,y})
		}
	}
}

func (pl *Player) checkCircle() {
	// na liniji
	if doesArrayContainPosition(fL, pl.getPosInt()) {
		if (pl.frFlag == true) {
			pl.laps--
			pl.flFlag = true
			pl.frFlag = false
		} else {
			pl.flFlag = true
		}
	// desno od linije
    } else if doesArrayContainPosition(fR, pl.getPosInt()) {
    	if (pl.flFlag == true) {
			pl.laps++
			pl.frFlag = true
			pl.flFlag = false
		} else {
			pl.frFlag = true
		}
	// drugje
    } else {
		pl.flFlag = false
		pl.frFlag = false
	}
}

func doesArrayContainPosition(fll []FinishLine, position int) bool {
	for _, fl := range fll {
		if position == fl.getPosInt() {
			return true
		}
	}
	return false
}

func toString(pl Player) string {
	return string(pl.Symbol) +" "+ string(pl.X) +" "+ string(pl.Y)
}

func getBoard() string {
	copy(tempBoard[:], board[0:len(board)])
	for _, pl := range players {
		pl.drawPlayerOnBoard(tempBoard)
	}
	return string(tempBoard)
}

func (pl *Player) drawPlayerOnBoard(boardLoc []rune) {
	var posInt = pl.getPosInt()
	boardLoc[posInt] = pl.Symbol
}

func getPosXY(pos int) (int, int) {
	var x = 0
	var y = 0
	for i := 0; i<=pos; i++ {
		x++
		if board[i] == '\n' {
			y++
			x = 0
		}
	}
	return x, y
}

func getPosInt(x int, y int) int {
	for i := 0; i<len(board); i++ {
		if board[i] == '\n' {
			y--
		}
		if y == 0 {
			x--
		}
		if y == 0 && x == 0 {
			return i
		}
	}
	panic("getPosInt: wrong coordinates")
}

func (pl *Player) getPosInt() int {
	return getPosInt(pl.X, pl.Y)
}

func (fl *FinishLine) getPosInt() int {
	return getPosInt(fl.X, fl.Y)
}

func (pl *Player) isMoveOk(move int) bool {
	var newPos = getPos(pl.X, pl.Y, move)
	var sym = board[newPos]
	if contains([]rune{' ', '|'}, sym) {
		if !ALLOW_BACK_OVER_LINE {
			return !headingBackOverLine(pl.getPosInt(), newPos)
		} else {
			return true
		}
	}
	return false
}

func headingBackOverLine(oldPos int, newPos int) bool {
	// v naslednji potezi bi na ciljni crti
	// in zdaj je desno od ciljne crte
	if 	doesArrayContainPosition(fL, newPos) &&
		doesArrayContainPosition(fR, oldPos) {
		return true
	}
    return false
}

func contains(arr []rune, sim rune) bool {
	for _, val := range arr {
		if val == sim {
			return true
		}
	}
	return false
}

func (pl *Player) executeMove(move int) {
	if move == 1 {
		pl.Y--
	}
	if move == 2 {
		pl.X++
	}
	if move == 3 {
		pl.Y++
	}
	if move == 4 {
		pl.X--
	}
	pl.checkCircle()
	globalChangeChanel <- 1
}

func getPos(x int, y int, move int) int {
	if move == 1 {
		return getPosInt(x, y-1)
	}
	if move == 2 {
		return getPosInt(x+1, y)
	}
	if move == 3 {
		return getPosInt(x, y+1)
	}
	if move == 4 {
		return getPosInt(x-1, y)
	}
	panic("Invalid move value!")
}

//1:up 2:right 3:down 4: left
//TODO make const
func getRandomMove() int {
    var rt = rand.Perm(4)
	var rrt = rt[1]+1
	return rrt
}

func wait(sec int) {
	var ssec = time.Duration(sec*1000000)
	time.Sleep(ssec)
}

func checkWin() bool{
   return false
}

<|endoftext|>