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

package os_test

import (
	"io/ioutil"
	. "os"
	"path/filepath"
	"runtime"
	"syscall"
	"testing"
)

func TestMkdirAll(t *testing.T) {
	tmpDir := TempDir()
	path := tmpDir + "/_TestMkdirAll_/dir/./dir2"
	err := MkdirAll(path, 0777)
	if err != nil {
		t.Fatalf("MkdirAll %q: %s", path, err)
	}
	defer RemoveAll(tmpDir + "/_TestMkdirAll_")

	// Already exists, should succeed.
	err = MkdirAll(path, 0777)
	if err != nil {
		t.Fatalf("MkdirAll %q (second time): %s", path, err)
	}

	// Make file.
	fpath := path + "/file"
	f, err := Create(fpath)
	if err != nil {
		t.Fatalf("create %q: %s", fpath, err)
	}
	defer f.Close()

	// Can't make directory named after file.
	err = MkdirAll(fpath, 0777)
	if err == nil {
		t.Fatalf("MkdirAll %q: no error", fpath)
	}
	perr, ok := err.(*PathError)
	if !ok {
		t.Fatalf("MkdirAll %q returned %T, not *PathError", fpath, err)
	}
	if filepath.Clean(perr.Path) != filepath.Clean(fpath) {
		t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", fpath, filepath.Clean(perr.Path), filepath.Clean(fpath))
	}

	// Can't make subdirectory of file.
	ffpath := fpath + "/subdir"
	err = MkdirAll(ffpath, 0777)
	if err == nil {
		t.Fatalf("MkdirAll %q: no error", ffpath)
	}
	perr, ok = err.(*PathError)
	if !ok {
		t.Fatalf("MkdirAll %q returned %T, not *PathError", ffpath, err)
	}
	if filepath.Clean(perr.Path) != filepath.Clean(fpath) {
		t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", ffpath, filepath.Clean(perr.Path), filepath.Clean(fpath))
	}

	if runtime.GOOS == "windows" {
		path := tmpDir + `\_TestMkdirAll_\dir\.\dir2\`
		err := MkdirAll(path, 0777)
		if err != nil {
			t.Fatalf("MkdirAll %q: %s", path, err)
		}
	}
}

func TestRemoveAll(t *testing.T) {
	tmpDir := TempDir()
	// Work directory.
	path := tmpDir + "/_TestRemoveAll_"
	fpath := path + "/file"
	dpath := path + "/dir"

	// Make directory with 1 file and remove.
	if err := MkdirAll(path, 0777); err != nil {
		t.Fatalf("MkdirAll %q: %s", path, err)
	}
	fd, err := Create(fpath)
	if err != nil {
		t.Fatalf("create %q: %s", fpath, err)
	}
	fd.Close()
	if err = RemoveAll(path); err != nil {
		t.Fatalf("RemoveAll %q (first): %s", path, err)
	}
	if _, err = Lstat(path); err == nil {
		t.Fatalf("Lstat %q succeeded after RemoveAll (first)", path)
	}

	// Make directory with file and subdirectory and remove.
	if err = MkdirAll(dpath, 0777); err != nil {
		t.Fatalf("MkdirAll %q: %s", dpath, err)
	}
	fd, err = Create(fpath)
	if err != nil {
		t.Fatalf("create %q: %s", fpath, err)
	}
	fd.Close()
	fd, err = Create(dpath + "/file")
	if err != nil {
		t.Fatalf("create %q: %s", fpath, err)
	}
	fd.Close()
	if err = RemoveAll(path); err != nil {
		t.Fatalf("RemoveAll %q (second): %s", path, err)
	}
	if _, err := Lstat(path); err == nil {
		t.Fatalf("Lstat %q succeeded after RemoveAll (second)", path)
	}

	// Determine if we should run the following test.
	testit := true
	if runtime.GOOS == "windows" {
		// Chmod is not supported under windows.
		testit = false
	} else {
		// Test fails as root.
		testit = Getuid() != 0
	}
	if testit {
		// Make directory with file and subdirectory and trigger error.
		if err = MkdirAll(dpath, 0777); err != nil {
			t.Fatalf("MkdirAll %q: %s", dpath, err)
		}

		for _, s := range []string{fpath, dpath + "/file1", path + "/zzz"} {
			fd, err = Create(s)
			if err != nil {
				t.Fatalf("create %q: %s", s, err)
			}
			fd.Close()
		}
		if err = Chmod(dpath, 0); err != nil {
			t.Fatalf("Chmod %q 0: %s", dpath, err)
		}

		// No error checking here: either RemoveAll
		// will or won't be able to remove dpath;
		// either way we want to see if it removes fpath
		// and path/zzz.  Reasons why RemoveAll might
		// succeed in removing dpath as well include:
		//	* running as root
		//	* running on a file system without permissions (FAT)
		RemoveAll(path)
		Chmod(dpath, 0777)

		for _, s := range []string{fpath, path + "/zzz"} {
			if _, err = Lstat(s); err == nil {
				t.Fatalf("Lstat %q succeeded after partial RemoveAll", s)
			}
		}
	}
	if err = RemoveAll(path); err != nil {
		t.Fatalf("RemoveAll %q after partial RemoveAll: %s", path, err)
	}
	if _, err = Lstat(path); err == nil {
		t.Fatalf("Lstat %q succeeded after RemoveAll (final)", path)
	}
}

func TestMkdirAllWithSymlink(t *testing.T) {
	switch runtime.GOOS {
	case "nacl", "plan9":
		t.Skipf("skipping on %s", runtime.GOOS)
	case "windows":
		if !supportsSymlinks {
			t.Skipf("skipping on %s", runtime.GOOS)
		}
	}

	tmpDir, err := ioutil.TempDir("", "TestMkdirAllWithSymlink-")
	if err != nil {
		t.Fatal(err)
	}
	defer RemoveAll(tmpDir)

	dir := tmpDir + "/dir"
	err = Mkdir(dir, 0755)
	if err != nil {
		t.Fatalf("Mkdir %s: %s", dir, err)
	}

	link := tmpDir + "/link"
	err = Symlink("dir", link)
	if err != nil {
		t.Fatalf("Symlink %s: %s", link, err)
	}

	path := link + "/foo"
	err = MkdirAll(path, 0755)
	if err != nil {
		t.Errorf("MkdirAll %q: %s", path, err)
	}
}

func TestMkdirAllAtSlash(t *testing.T) {
	switch runtime.GOOS {
	case "android", "plan9", "windows":
		t.Skipf("skipping on %s", runtime.GOOS)
	}
	RemoveAll("/_go_os_test")
	err := MkdirAll("/_go_os_test/dir", 0777)
	if err != nil {
		pathErr, ok := err.(*PathError)
		// common for users not to be able to write to /
		if ok && (pathErr.Err == syscall.EACCES || pathErr.Err == syscall.EROFS) {
			return
		}
		t.Fatalf(`MkdirAll "/_go_os_test/dir": %v`, err)
	}
	RemoveAll("/_go_os_test")
}
<commit_msg>os: don't silently skip test<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package os_test

import (
	"io/ioutil"
	. "os"
	"path/filepath"
	"runtime"
	"syscall"
	"testing"
)

func TestMkdirAll(t *testing.T) {
	tmpDir := TempDir()
	path := tmpDir + "/_TestMkdirAll_/dir/./dir2"
	err := MkdirAll(path, 0777)
	if err != nil {
		t.Fatalf("MkdirAll %q: %s", path, err)
	}
	defer RemoveAll(tmpDir + "/_TestMkdirAll_")

	// Already exists, should succeed.
	err = MkdirAll(path, 0777)
	if err != nil {
		t.Fatalf("MkdirAll %q (second time): %s", path, err)
	}

	// Make file.
	fpath := path + "/file"
	f, err := Create(fpath)
	if err != nil {
		t.Fatalf("create %q: %s", fpath, err)
	}
	defer f.Close()

	// Can't make directory named after file.
	err = MkdirAll(fpath, 0777)
	if err == nil {
		t.Fatalf("MkdirAll %q: no error", fpath)
	}
	perr, ok := err.(*PathError)
	if !ok {
		t.Fatalf("MkdirAll %q returned %T, not *PathError", fpath, err)
	}
	if filepath.Clean(perr.Path) != filepath.Clean(fpath) {
		t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", fpath, filepath.Clean(perr.Path), filepath.Clean(fpath))
	}

	// Can't make subdirectory of file.
	ffpath := fpath + "/subdir"
	err = MkdirAll(ffpath, 0777)
	if err == nil {
		t.Fatalf("MkdirAll %q: no error", ffpath)
	}
	perr, ok = err.(*PathError)
	if !ok {
		t.Fatalf("MkdirAll %q returned %T, not *PathError", ffpath, err)
	}
	if filepath.Clean(perr.Path) != filepath.Clean(fpath) {
		t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", ffpath, filepath.Clean(perr.Path), filepath.Clean(fpath))
	}

	if runtime.GOOS == "windows" {
		path := tmpDir + `\_TestMkdirAll_\dir\.\dir2\`
		err := MkdirAll(path, 0777)
		if err != nil {
			t.Fatalf("MkdirAll %q: %s", path, err)
		}
	}
}

func TestRemoveAll(t *testing.T) {
	tmpDir := TempDir()
	// Work directory.
	path := tmpDir + "/_TestRemoveAll_"
	fpath := path + "/file"
	dpath := path + "/dir"

	// Make directory with 1 file and remove.
	if err := MkdirAll(path, 0777); err != nil {
		t.Fatalf("MkdirAll %q: %s", path, err)
	}
	fd, err := Create(fpath)
	if err != nil {
		t.Fatalf("create %q: %s", fpath, err)
	}
	fd.Close()
	if err = RemoveAll(path); err != nil {
		t.Fatalf("RemoveAll %q (first): %s", path, err)
	}
	if _, err = Lstat(path); err == nil {
		t.Fatalf("Lstat %q succeeded after RemoveAll (first)", path)
	}

	// Make directory with file and subdirectory and remove.
	if err = MkdirAll(dpath, 0777); err != nil {
		t.Fatalf("MkdirAll %q: %s", dpath, err)
	}
	fd, err = Create(fpath)
	if err != nil {
		t.Fatalf("create %q: %s", fpath, err)
	}
	fd.Close()
	fd, err = Create(dpath + "/file")
	if err != nil {
		t.Fatalf("create %q: %s", fpath, err)
	}
	fd.Close()
	if err = RemoveAll(path); err != nil {
		t.Fatalf("RemoveAll %q (second): %s", path, err)
	}
	if _, err := Lstat(path); err == nil {
		t.Fatalf("Lstat %q succeeded after RemoveAll (second)", path)
	}

	// Determine if we should run the following test.
	testit := true
	if runtime.GOOS == "windows" {
		// Chmod is not supported under windows.
		testit = false
	} else {
		// Test fails as root.
		testit = Getuid() != 0
	}
	if testit {
		// Make directory with file and subdirectory and trigger error.
		if err = MkdirAll(dpath, 0777); err != nil {
			t.Fatalf("MkdirAll %q: %s", dpath, err)
		}

		for _, s := range []string{fpath, dpath + "/file1", path + "/zzz"} {
			fd, err = Create(s)
			if err != nil {
				t.Fatalf("create %q: %s", s, err)
			}
			fd.Close()
		}
		if err = Chmod(dpath, 0); err != nil {
			t.Fatalf("Chmod %q 0: %s", dpath, err)
		}

		// No error checking here: either RemoveAll
		// will or won't be able to remove dpath;
		// either way we want to see if it removes fpath
		// and path/zzz.  Reasons why RemoveAll might
		// succeed in removing dpath as well include:
		//	* running as root
		//	* running on a file system without permissions (FAT)
		RemoveAll(path)
		Chmod(dpath, 0777)

		for _, s := range []string{fpath, path + "/zzz"} {
			if _, err = Lstat(s); err == nil {
				t.Fatalf("Lstat %q succeeded after partial RemoveAll", s)
			}
		}
	}
	if err = RemoveAll(path); err != nil {
		t.Fatalf("RemoveAll %q after partial RemoveAll: %s", path, err)
	}
	if _, err = Lstat(path); err == nil {
		t.Fatalf("Lstat %q succeeded after RemoveAll (final)", path)
	}
}

func TestMkdirAllWithSymlink(t *testing.T) {
	switch runtime.GOOS {
	case "nacl", "plan9":
		t.Skipf("skipping on %s", runtime.GOOS)
	case "windows":
		if !supportsSymlinks {
			t.Skipf("skipping on %s", runtime.GOOS)
		}
	}

	tmpDir, err := ioutil.TempDir("", "TestMkdirAllWithSymlink-")
	if err != nil {
		t.Fatal(err)
	}
	defer RemoveAll(tmpDir)

	dir := tmpDir + "/dir"
	err = Mkdir(dir, 0755)
	if err != nil {
		t.Fatalf("Mkdir %s: %s", dir, err)
	}

	link := tmpDir + "/link"
	err = Symlink("dir", link)
	if err != nil {
		t.Fatalf("Symlink %s: %s", link, err)
	}

	path := link + "/foo"
	err = MkdirAll(path, 0755)
	if err != nil {
		t.Errorf("MkdirAll %q: %s", path, err)
	}
}

func TestMkdirAllAtSlash(t *testing.T) {
	switch runtime.GOOS {
	case "android", "plan9", "windows":
		t.Skipf("skipping on %s", runtime.GOOS)
	}
	RemoveAll("/_go_os_test")
	const dir = "/go_os_test/dir"
	err := MkdirAll(dir, 0777)
	if err != nil {
		pathErr, ok := err.(*PathError)
		// common for users not to be able to write to /
		if ok && (pathErr.Err == syscall.EACCES || pathErr.Err == syscall.EROFS) {
			t.Skipf("could not create %v: %v", dir, err)
		}
		t.Fatalf(`MkdirAll "/_go_os_test/dir": %v`, err)
	}
	RemoveAll("/_go_os_test")
}
<|endoftext|>


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

package net

import "os"

// Dial connects to the address addr on the network net.
//
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
// (IPv4-only), "ip6" (IPv6-only), "unix" and "unixgram".
//
// For IP networks, addresses have the form host:port.  If host is
// a literal IPv6 address, it must be enclosed in square brackets.
// The functions JoinHostPort and SplitHostPort manipulate
// addresses in this form.
//
// Examples:
//	Dial("tcp", "12.34.56.78:80")
//	Dial("tcp", "google.com:80")
//	Dial("tcp", "[de:ad:be:ef::ca:fe]:80")
//
func Dial(net, addr string) (c Conn, err os.Error) {
	raddr := addr
	if raddr == "" {
		return nil, &OpError{"dial", net, nil, errMissingAddress}
	}
	switch net {
	case "tcp", "tcp4", "tcp6":
		var ra *TCPAddr
		if ra, err = ResolveTCPAddr(net, raddr); err != nil {
			goto Error
		}
		c, err := DialTCP(net, nil, ra)
		if err != nil {
			return nil, err
		}
		return c, nil
	case "udp", "udp4", "udp6":
		var ra *UDPAddr
		if ra, err = ResolveUDPAddr(net, raddr); err != nil {
			goto Error
		}
		c, err := DialUDP(net, nil, ra)
		if err != nil {
			return nil, err
		}
		return c, nil
	case "unix", "unixgram", "unixpacket":
		var ra *UnixAddr
		if ra, err = ResolveUnixAddr(net, raddr); err != nil {
			goto Error
		}
		c, err = DialUnix(net, nil, ra)
		if err != nil {
			return nil, err
		}
		return c, nil
	case "ip", "ip4", "ip6":
		var ra *IPAddr
		if ra, err = ResolveIPAddr(net, raddr); err != nil {
			goto Error
		}
		c, err := DialIP(net, nil, ra)
		if err != nil {
			return nil, err
		}
		return c, nil

	}
	err = UnknownNetworkError(net)
Error:
	return nil, &OpError{"dial", net + " " + raddr, nil, err}
}

// Listen announces on the local network address laddr.
// The network string net must be a stream-oriented
// network: "tcp", "tcp4", "tcp6", or "unix", or "unixpacket".
func Listen(net, laddr string) (l Listener, err os.Error) {
	switch net {
	case "tcp", "tcp4", "tcp6":
		var la *TCPAddr
		if laddr != "" {
			if la, err = ResolveTCPAddr(net, laddr); err != nil {
				return nil, err
			}
		}
		l, err := ListenTCP(net, la)
		if err != nil {
			return nil, err
		}
		return l, nil
	case "unix", "unixpacket":
		var la *UnixAddr
		if laddr != "" {
			if la, err = ResolveUnixAddr(net, laddr); err != nil {
				return nil, err
			}
		}
		l, err := ListenUnix(net, la)
		if err != nil {
			return nil, err
		}
		return l, nil
	}
	return nil, UnknownNetworkError(net)
}

// ListenPacket announces on the local network address laddr.
// The network string net must be a packet-oriented network:
// "udp", "udp4", "udp6", or "unixgram".
func ListenPacket(net, laddr string) (c PacketConn, err os.Error) {
	switch net {
	case "udp", "udp4", "udp6":
		var la *UDPAddr
		if laddr != "" {
			if la, err = ResolveUDPAddr(net, laddr); err != nil {
				return nil, err
			}
		}
		c, err := ListenUDP(net, la)
		if err != nil {
			return nil, err
		}
		return c, nil
	case "unixgram":
		var la *UnixAddr
		if laddr != "" {
			if la, err = ResolveUnixAddr(net, laddr); err != nil {
				return nil, err
			}
		}
		c, err := DialUnix(net, la, nil)
		if err != nil {
			return nil, err
		}
		return c, nil
	}

	var rawnet string
	if rawnet, _, err = splitNetProto(net); err != nil {
		switch rawnet {
		case "ip", "ip4", "ip6":
			var la *IPAddr
			if laddr != "" {
				if la, err = ResolveIPAddr(rawnet, laddr); err != nil {
					return nil, err
				}
			}
			c, err := ListenIP(net, la)
			if err != nil {
				return nil, err
			}
			return c, nil
		}
	}

	return nil, UnknownNetworkError(net)
}
<commit_msg>net: break up and simplify Dial a bit<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package net

import "os"

func resolveNetAddr(op, net, addr string) (a Addr, err os.Error) {
	if addr == "" {
		return nil, &OpError{op, net, nil, errMissingAddress}
	}
	switch net {
	case "tcp", "tcp4", "tcp6":
		a, err = ResolveTCPAddr(net, addr)
	case "udp", "udp4", "udp6":
		a, err = ResolveUDPAddr(net, addr)
	case "unix", "unixgram", "unixpacket":
		a, err = ResolveUnixAddr(net, addr)
	case "ip", "ip4", "ip6":
		a, err = ResolveIPAddr(net, addr)
	default:
		err = UnknownNetworkError(net)
	}
	if err != nil {
		return nil, &OpError{op, net + " " + addr, nil, err}
	}
	return
}

// Dial connects to the address addr on the network net.
//
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
// (IPv4-only), "ip6" (IPv6-only), "unix" and "unixgram".
//
// For IP networks, addresses have the form host:port.  If host is
// a literal IPv6 address, it must be enclosed in square brackets.
// The functions JoinHostPort and SplitHostPort manipulate
// addresses in this form.
//
// Examples:
//	Dial("tcp", "12.34.56.78:80")
//	Dial("tcp", "google.com:80")
//	Dial("tcp", "[de:ad:be:ef::ca:fe]:80")
//
func Dial(net, addr string) (c Conn, err os.Error) {
	addri, err := resolveNetAddr("dial", net, addr)
	if err != nil {
		return nil, err
	}
	switch ra := addri.(type) {
	case *TCPAddr:
		c, err = DialTCP(net, nil, ra)
	case *UDPAddr:
		c, err = DialUDP(net, nil, ra)
	case *UnixAddr:
		c, err = DialUnix(net, nil, ra)
	case *IPAddr:
		c, err = DialIP(net, nil, ra)
	default:
		err = UnknownNetworkError(net)
	}
	if err != nil {
		return nil, &OpError{"dial", net + " " + addr, nil, err}
	}
	return
}

// Listen announces on the local network address laddr.
// The network string net must be a stream-oriented
// network: "tcp", "tcp4", "tcp6", or "unix", or "unixpacket".
func Listen(net, laddr string) (l Listener, err os.Error) {
	switch net {
	case "tcp", "tcp4", "tcp6":
		var la *TCPAddr
		if laddr != "" {
			if la, err = ResolveTCPAddr(net, laddr); err != nil {
				return nil, err
			}
		}
		l, err := ListenTCP(net, la)
		if err != nil {
			return nil, err
		}
		return l, nil
	case "unix", "unixpacket":
		var la *UnixAddr
		if laddr != "" {
			if la, err = ResolveUnixAddr(net, laddr); err != nil {
				return nil, err
			}
		}
		l, err := ListenUnix(net, la)
		if err != nil {
			return nil, err
		}
		return l, nil
	}
	return nil, UnknownNetworkError(net)
}

// ListenPacket announces on the local network address laddr.
// The network string net must be a packet-oriented network:
// "udp", "udp4", "udp6", or "unixgram".
func ListenPacket(net, laddr string) (c PacketConn, err os.Error) {
	switch net {
	case "udp", "udp4", "udp6":
		var la *UDPAddr
		if laddr != "" {
			if la, err = ResolveUDPAddr(net, laddr); err != nil {
				return nil, err
			}
		}
		c, err := ListenUDP(net, la)
		if err != nil {
			return nil, err
		}
		return c, nil
	case "unixgram":
		var la *UnixAddr
		if laddr != "" {
			if la, err = ResolveUnixAddr(net, laddr); err != nil {
				return nil, err
			}
		}
		c, err := DialUnix(net, la, nil)
		if err != nil {
			return nil, err
		}
		return c, nil
	}

	var rawnet string
	if rawnet, _, err = splitNetProto(net); err != nil {
		switch rawnet {
		case "ip", "ip4", "ip6":
			var la *IPAddr
			if laddr != "" {
				if la, err = ResolveIPAddr(rawnet, laddr); err != nil {
					return nil, err
				}
			}
			c, err := ListenIP(net, la)
			if err != nil {
				return nil, err
			}
			return c, nil
		}
	}

	return nil, UnknownNetworkError(net)
}
<|endoftext|>


<commit_before>package dev

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"sync"
	"time"

	"github.com/fsnotify/fsevents"
	"gopkg.in/tomb.v2"
)

const DefaultThreads = 5

var ErrUnexpectedExit = errors.New("unexpected exit")

type App struct {
	Name    string
	Scheme  string
	Host    string
	Port    int
	Command *exec.Cmd

	address string
	dir     string

	t tomb.Tomb

	stdout  io.Reader
	lock    sync.Mutex
	pool    *AppPool
	lastUse time.Time
}

func (a *App) SetAddress(scheme, host string, port int) {
	a.Scheme = scheme
	a.Host = host
	a.Port = port

	if a.Port == 0 {
		a.address = host
	} else {
		a.address = fmt.Sprintf("%s:%d", a.Host, a.Port)
	}
}

func (a *App) Address() string {
	if a.Port == 0 {
		return a.Host
	}

	return fmt.Sprintf("%s:%d", a.Host, a.Port)
}

func (a *App) Kill() error {
	fmt.Printf("! Killing '%s' (%d)\n", a.Name, a.Command.Process.Pid)
	err := a.Command.Process.Kill()
	if err != nil {
		fmt.Printf("! Error trying to kill %s: %s", a.Name, err)
	}
	return err
}

func (a *App) watch() error {
	c := make(chan error)

	go func() {
		r := bufio.NewReader(a.stdout)

		for {
			line, err := r.ReadString('\n')
			if line != "" {
				fmt.Fprintf(os.Stdout, "%s[%d]: %s", a.Name, a.Command.Process.Pid, line)
			}

			if err != nil {
				c <- err
				return
			}
		}
	}()

	var err error

	select {
	case err = <-c:
		err = ErrUnexpectedExit
	case <-a.t.Dying():
		a.Kill()
		err = nil
	}

	a.Command.Wait()
	a.pool.remove(a)

	if a.Scheme == "httpu" {
		os.Remove(a.Address())
	}

	fmt.Printf("* App '%s' shutdown and cleaned up\n", a.Name)

	return err
}

func (a *App) idleMonitor() error {
	ticker := time.NewTicker(10 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			if a.pool.maybeIdle(a) {
				a.Kill()
			}
			return nil
		case <-a.t.Dying():
			return nil
		}
	}

	return nil
}

func (a *App) restartMonitor() error {
	tmpDir := filepath.Join(a.dir, "tmp")
	err := os.MkdirAll(tmpDir, 0755)
	if err != nil {
		return err
	}

	restart := filepath.Join(tmpDir, "restart.txt")

	f, err := os.Create(restart)
	if err != nil {
		return err
	}
	f.Close()

	dev, err := fsevents.DeviceForPath(restart)
	if err != nil {
		return err
	}

	es := &fsevents.EventStream{
		Paths:   []string{restart},
		Latency: 500 * time.Millisecond,
		Device:  dev,
		Flags:   fsevents.FileEvents | fsevents.IgnoreSelf,
	}

	es.Start()

	defer es.Stop()

	for {
		select {
		case events := <-es.Events:
			for _, ev := range events {
				if ev.Flags&fsevents.ItemInodeMetaMod != 0 {
					a.Kill()
				}
			}
		case <-a.t.Dying():
			return nil
		}
	}
}

func (a *App) UpdateUsed() {
	a.lastUse = time.Now()
}

const executionShell = `# puma-dev generated at runtime
if test -e ~/.powconfig; then
	source ~/.powconfig
fi

if test -e .env; then
	source .env
fi

if test -e .powrc; then
	source .powrc
fi

if test -e .powenv; then
	source .powenv
fi

if test -e Gemfile; then
	exec bundle exec puma -C $CONFIG --tag puma-dev:%s -w $WORKERS -t 0:$THREADS -b unix:%s
fi

exec puma -C $CONFIG --tag puma-dev:%s -w $WORKERS -t 0:$THREADS -b unix:%s
`

func LaunchApp(pool *AppPool, name, dir string) (*App, error) {
	tmpDir := filepath.Join(dir, "tmp")
	err := os.MkdirAll(tmpDir, 0755)
	if err != nil {
		return nil, err
	}

	socket := filepath.Join(tmpDir, fmt.Sprintf("puma-dev-%d.sock", os.Getpid()))

	shell := os.Getenv("SHELL")

	cmd := exec.Command(shell, "-l", "-i", "-c",
		fmt.Sprintf(executionShell, name, socket, name, socket))

	cmd.Dir = dir

	cmd.Env = os.Environ()
	cmd.Env = append(cmd.Env,
		fmt.Sprintf("THREADS=%d", DefaultThreads),
		"WORKERS=0",
		"CONFIG=-",
	)

	cmd.Stderr = os.Stderr

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

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

	fmt.Printf("! Booted app '%s' on socket %s\n", name, socket)

	app := &App{
		Name:    name,
		Command: cmd,
		stdout:  stdout,
		dir:     dir,
		pool:    pool,
	}

	app.SetAddress("httpu", socket, 0)

	app.t.Go(app.watch)
	app.t.Go(app.idleMonitor)
	app.t.Go(app.restartMonitor)

	// This is a poor substitute for getting an actual readiness signal
	// from puma but it's good enough.
	for {
		c, err := net.Dial("unix", socket)
		if err == nil {
			c.Close()
			break
		}

		time.Sleep(250 * time.Microsecond)
	}

	return app, nil
}

type AppPool struct {
	Dir      string
	IdleTime time.Duration

	lock sync.Mutex
	apps map[string]*App
}

func (a *AppPool) maybeIdle(app *App) bool {
	a.lock.Lock()
	defer a.lock.Unlock()

	diff := time.Since(app.lastUse)
	if diff > a.IdleTime {
		delete(a.apps, app.Name)
		return true
	}

	return false
}

var ErrUnknownApp = errors.New("unknown app")

func (a *AppPool) App(name string) (*App, error) {
	a.lock.Lock()
	defer a.lock.Unlock()

	if a.apps == nil {
		a.apps = make(map[string]*App)
	}

	app, ok := a.apps[name]
	if ok {
		app.UpdateUsed()
		return app, nil
	}

	path := filepath.Join(a.Dir, name)

	stat, err := os.Stat(path)
	if os.IsNotExist(err) {
		return nil, ErrUnknownApp
	}

	if stat.IsDir() {
		app, err = LaunchApp(a, name, path)
		if err != nil {
			return nil, err
		}
	} else {
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return nil, err
		}

		app = &App{
			Name: name,
		}

		data = bytes.TrimSpace(data)

		port, err := strconv.Atoi(string(data))
		if err == nil {
			app.SetAddress("http", "127.0.0.1", port)
		} else {
			u, err := url.Parse(string(data))
			if err != nil {
				return nil, err
			}

			var (
				sport, host string
				port        int
			)

			host, sport, err = net.SplitHostPort(u.Host)
			if err == nil {
				port, err = strconv.Atoi(sport)
				if err != nil {
					return nil, err
				}
			} else {
				host = u.Host
			}

			app.SetAddress(u.Scheme, host, port)
		}

		fmt.Printf("* Generated proxy connection for '%s' to %s://%s\n",
			name, app.Scheme, app.Address())
	}

	app.pool = a

	app.UpdateUsed()
	a.apps[name] = app

	return app, nil
}

func (a *AppPool) remove(app *App) {
	a.lock.Lock()
	defer a.lock.Unlock()

	delete(a.apps, app.Name)
}

func (a *AppPool) Purge() {
	a.lock.Lock()

	var apps []*App

	for _, app := range a.apps {
		apps = append(apps, app)
	}

	a.lock.Unlock()

	for _, app := range apps {
		app.t.Kill(nil)
	}

	for _, app := range apps {
		app.t.Wait()
	}
}
<commit_msg>Fix stopping idle apps<commit_after>package dev

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"sync"
	"time"

	"github.com/fsnotify/fsevents"
	"gopkg.in/tomb.v2"
)

const DefaultThreads = 5

var ErrUnexpectedExit = errors.New("unexpected exit")

type App struct {
	Name    string
	Scheme  string
	Host    string
	Port    int
	Command *exec.Cmd

	address string
	dir     string

	t tomb.Tomb

	stdout  io.Reader
	lock    sync.Mutex
	pool    *AppPool
	lastUse time.Time
}

func (a *App) SetAddress(scheme, host string, port int) {
	a.Scheme = scheme
	a.Host = host
	a.Port = port

	if a.Port == 0 {
		a.address = host
	} else {
		a.address = fmt.Sprintf("%s:%d", a.Host, a.Port)
	}
}

func (a *App) Address() string {
	if a.Port == 0 {
		return a.Host
	}

	return fmt.Sprintf("%s:%d", a.Host, a.Port)
}

func (a *App) Kill() error {
	fmt.Printf("! Killing '%s' (%d)\n", a.Name, a.Command.Process.Pid)
	err := a.Command.Process.Kill()
	if err != nil {
		fmt.Printf("! Error trying to kill %s: %s", a.Name, err)
	}
	return err
}

func (a *App) watch() error {
	c := make(chan error)

	go func() {
		r := bufio.NewReader(a.stdout)

		for {
			line, err := r.ReadString('\n')
			if line != "" {
				fmt.Fprintf(os.Stdout, "%s[%d]: %s", a.Name, a.Command.Process.Pid, line)
			}

			if err != nil {
				c <- err
				return
			}
		}
	}()

	var err error

	select {
	case err = <-c:
		err = ErrUnexpectedExit
	case <-a.t.Dying():
		a.Kill()
		err = nil
	}

	a.Command.Wait()
	a.pool.remove(a)

	if a.Scheme == "httpu" {
		os.Remove(a.Address())
	}

	fmt.Printf("* App '%s' shutdown and cleaned up\n", a.Name)

	return err
}

func (a *App) idleMonitor() error {
	ticker := time.NewTicker(10 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			if a.pool.maybeIdle(a) {
				a.Kill()
				return nil
			}
		case <-a.t.Dying():
			return nil
		}
	}

	return nil
}

func (a *App) restartMonitor() error {
	tmpDir := filepath.Join(a.dir, "tmp")
	err := os.MkdirAll(tmpDir, 0755)
	if err != nil {
		return err
	}

	restart := filepath.Join(tmpDir, "restart.txt")

	f, err := os.Create(restart)
	if err != nil {
		return err
	}
	f.Close()

	dev, err := fsevents.DeviceForPath(restart)
	if err != nil {
		return err
	}

	es := &fsevents.EventStream{
		Paths:   []string{restart},
		Latency: 500 * time.Millisecond,
		Device:  dev,
		Flags:   fsevents.FileEvents | fsevents.IgnoreSelf,
	}

	es.Start()

	defer es.Stop()

	for {
		select {
		case events := <-es.Events:
			for _, ev := range events {
				if ev.Flags&fsevents.ItemInodeMetaMod != 0 {
					a.Kill()
				}
			}
		case <-a.t.Dying():
			return nil
		}
	}
}

func (a *App) UpdateUsed() {
	a.lastUse = time.Now()
}

const executionShell = `# puma-dev generated at runtime
if test -e ~/.powconfig; then
	source ~/.powconfig
fi

if test -e .env; then
	source .env
fi

if test -e .powrc; then
	source .powrc
fi

if test -e .powenv; then
	source .powenv
fi

if test -e Gemfile; then
	exec bundle exec puma -C $CONFIG --tag puma-dev:%s -w $WORKERS -t 0:$THREADS -b unix:%s
fi

exec puma -C $CONFIG --tag puma-dev:%s -w $WORKERS -t 0:$THREADS -b unix:%s
`

func LaunchApp(pool *AppPool, name, dir string) (*App, error) {
	tmpDir := filepath.Join(dir, "tmp")
	err := os.MkdirAll(tmpDir, 0755)
	if err != nil {
		return nil, err
	}

	socket := filepath.Join(tmpDir, fmt.Sprintf("puma-dev-%d.sock", os.Getpid()))

	shell := os.Getenv("SHELL")

	cmd := exec.Command(shell, "-l", "-i", "-c",
		fmt.Sprintf(executionShell, name, socket, name, socket))

	cmd.Dir = dir

	cmd.Env = os.Environ()
	cmd.Env = append(cmd.Env,
		fmt.Sprintf("THREADS=%d", DefaultThreads),
		"WORKERS=0",
		"CONFIG=-",
	)

	cmd.Stderr = os.Stderr

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

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

	fmt.Printf("! Booted app '%s' on socket %s\n", name, socket)

	app := &App{
		Name:    name,
		Command: cmd,
		stdout:  stdout,
		dir:     dir,
		pool:    pool,
	}

	app.SetAddress("httpu", socket, 0)

	app.t.Go(app.watch)
	app.t.Go(app.idleMonitor)
	app.t.Go(app.restartMonitor)

	// This is a poor substitute for getting an actual readiness signal
	// from puma but it's good enough.
	for {
		c, err := net.Dial("unix", socket)
		if err == nil {
			c.Close()
			break
		}

		time.Sleep(250 * time.Microsecond)
	}

	return app, nil
}

type AppPool struct {
	Dir      string
	IdleTime time.Duration

	lock sync.Mutex
	apps map[string]*App
}

func (a *AppPool) maybeIdle(app *App) bool {
	a.lock.Lock()
	defer a.lock.Unlock()

	diff := time.Since(app.lastUse)
	if diff > a.IdleTime {
		delete(a.apps, app.Name)
		return true
	}

	return false
}

var ErrUnknownApp = errors.New("unknown app")

func (a *AppPool) App(name string) (*App, error) {
	a.lock.Lock()
	defer a.lock.Unlock()

	if a.apps == nil {
		a.apps = make(map[string]*App)
	}

	app, ok := a.apps[name]
	if ok {
		app.UpdateUsed()
		return app, nil
	}

	path := filepath.Join(a.Dir, name)

	stat, err := os.Stat(path)
	if os.IsNotExist(err) {
		return nil, ErrUnknownApp
	}

	if stat.IsDir() {
		app, err = LaunchApp(a, name, path)
		if err != nil {
			return nil, err
		}
	} else {
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return nil, err
		}

		app = &App{
			Name: name,
		}

		data = bytes.TrimSpace(data)

		port, err := strconv.Atoi(string(data))
		if err == nil {
			app.SetAddress("http", "127.0.0.1", port)
		} else {
			u, err := url.Parse(string(data))
			if err != nil {
				return nil, err
			}

			var (
				sport, host string
				port        int
			)

			host, sport, err = net.SplitHostPort(u.Host)
			if err == nil {
				port, err = strconv.Atoi(sport)
				if err != nil {
					return nil, err
				}
			} else {
				host = u.Host
			}

			app.SetAddress(u.Scheme, host, port)
		}

		fmt.Printf("* Generated proxy connection for '%s' to %s://%s\n",
			name, app.Scheme, app.Address())
	}

	app.pool = a

	app.UpdateUsed()
	a.apps[name] = app

	return app, nil
}

func (a *AppPool) remove(app *App) {
	a.lock.Lock()
	defer a.lock.Unlock()

	delete(a.apps, app.Name)
}

func (a *AppPool) Purge() {
	a.lock.Lock()

	var apps []*App

	for _, app := range a.apps {
		apps = append(apps, app)
	}

	a.lock.Unlock()

	for _, app := range apps {
		app.t.Kill(nil)
	}

	for _, app := range apps {
		app.t.Wait()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/base64"
	"encoding/json"
	"github.com/nicksnyder/go-i18n/i18n"
	"html/template"
	"net/http"
	"os"
	"path"
	"strings"
)

/**
 * JSON files containing information about articles stored in the distributed cache (Freenet)
 * are named like `json-files/<base64(feed's url)>.json`
 * @param feedUrl - The URL of the RSS/Atom feed to retrieve information about articles from
 */
func articlesFilename(feedUrl string) string {
	b64FeedUrl := base64.StdEncoding.EncodeToString([]byte(feedUrl))
	return path.Join(".", "json-files", b64FeedUrl+".json")
}

/**
 * Get information about articles from a given feed to be injected into the portal page.
 * @param {string} feedUrl - The URL of the feed to fetch articles from
 * @return a map with a "feeds" key and corresponding array of Feed structs and an optional error
 */
func initModuleWithArticles(feedUrl string) (map[string]interface{}, error) {
	articleInfoFile, openErr := os.Open(articlesFilename(feedUrl))
	if openErr != nil {
		return nil, openErr
	}
	defer articleInfoFile.Close()
	articleInfo := ArticleInfo{}
	decoder := json.NewDecoder(articleInfoFile)
	decodeErr := decoder.Decode(&articleInfo)
	if decodeErr != nil {
		return nil, decodeErr
	}
	mapping := make(map[string]interface{})
	mapping["articles"] = articleInfo.Items
	return mapping, nil
}

/**
 * Build the articles template with links to articles in a particular feed.
 */
func CreateArticlePage(w http.ResponseWriter, r *http.Request) {
	T, _ := i18n.Tfunc(os.Getenv(LANG_ENVVAR), DEFAULT_LANG)
	t, _ := template.ParseFiles(path.Join(".", "views", "articles.html"))
	pathComponents := strings.Split(r.URL.Path, "/")
	b64FeedUrl := pathComponents[len(pathComponents)-1]
	feedUrlBytes, _ := base64.StdEncoding.DecodeString(b64FeedUrl)
	feedUrl := string(feedUrlBytes)
	moduleData, articlesErr := initModuleWithArticles(feedUrl)
	if articlesErr != nil {
		HandleCCError(ERR_NO_ARTICLES_FILE, articlesErr.Error(), ErrorState{
			"responseWriter": w,
			"request":        r,
		})
		return
	}
	moduleData["authorWord"] = T("authors_word")
	moduleData["publishedWord"] = T("published_word")
	marshalled, err := json.Marshal(moduleData)
	var module string
	if err != nil {
		HandleCCError(ERR_CORRUPT_JSON, err.Error(), ErrorState{
			"responseWriter": w,
			"request":        r,
		})
		return
	}
	module = string(marshalled[:])
	t.Execute(w, map[string]interface{}{
		"Previous":         T("previous_word"),
		"More":             T("more_word"),
		"PortalBlurb":      T("portal_blurb"),
		"CenoPortalModule": module,
	})
}
<commit_msg>Lookup the latest articles page before defaulting to the files we were distributed with<commit_after>package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"github.com/nicksnyder/go-i18n/i18n"
	"html/template"
	"net/http"
	"os"
	"path"
	"strings"
)

/**
 * JSON files containing information about articles stored in the distributed cache (Freenet)
 * are named like `json-files/<base64(feed's url)>.json`
 * @param feedUrl - The URL of the RSS/Atom feed to retrieve information about articles from
 */
func articlesFilename(feedUrl string) string {
	b64FeedUrl := base64.StdEncoding.EncodeToString([]byte(feedUrl))
	return path.Join(".", "json-files", b64FeedUrl+".json")
}

/**
 * Get information about articles from a given feed to be injected into the portal page.
 * @param {string} feedUrl - The URL of the feed to fetch articles from
 * @return a map with a "feeds" key and corresponding array of Feed structs and an optional error
 */
func initModuleWithArticles(feedUrl string) (map[string]interface{}, error) {
	articleInfo := ArticleInfo{}
	var decodeErr error
	result := Lookup(feedUrl)
	if result.Complete {
		// Serve whatever the LCS gave us as the most recent articles list for
		// the feed we want to see.
		decoder := json.NewDecoder(bytes.NewReader([]byte(result.Bundle)))
		decodeErr = decoder.Decode(&articleInfo)
	} else {
		// Before the first complete lookup, serve from the files
		// distributed with the client.
		articleInfoFile, openErr := os.Open(articlesFilename(feedUrl))
		if openErr != nil {
			return nil, openErr
		}
		defer articleInfoFile.Close()
		decoder := json.NewDecoder(articleInfoFile)
		decodeErr = decoder.Decode(&articleInfo)
	}
	if decodeErr != nil {
		return nil, decodeErr
	}
	mapping := make(map[string]interface{})
	mapping["articles"] = articleInfo.Items
	return mapping, nil
}

/**
 * Build the articles template with links to articles in a particular feed.
 */
func CreateArticlePage(w http.ResponseWriter, r *http.Request) {
	T, _ := i18n.Tfunc(os.Getenv(LANG_ENVVAR), DEFAULT_LANG)
	t, _ := template.ParseFiles(path.Join(".", "views", "articles.html"))
	pathComponents := strings.Split(r.URL.Path, "/")
	b64FeedUrl := pathComponents[len(pathComponents)-1]
	feedUrlBytes, _ := base64.StdEncoding.DecodeString(b64FeedUrl)
	feedUrl := string(feedUrlBytes)
	moduleData, articlesErr := initModuleWithArticles(feedUrl)
	if articlesErr != nil {
		HandleCCError(ERR_NO_ARTICLES_FILE, articlesErr.Error(), ErrorState{
			"responseWriter": w,
			"request":        r,
		})
		return
	}
	moduleData["authorWord"] = T("authors_word")
	moduleData["publishedWord"] = T("published_word")
	marshalled, err := json.Marshal(moduleData)
	var module string
	if err != nil {
		HandleCCError(ERR_CORRUPT_JSON, err.Error(), ErrorState{
			"responseWriter": w,
			"request":        r,
		})
		return
	}
	module = string(marshalled[:])
	t.Execute(w, map[string]interface{}{
		"Previous":         T("previous_word"),
		"More":             T("more_word"),
		"PortalBlurb":      T("portal_blurb"),
		"CenoPortalModule": module,
	})
}
<|endoftext|>


<commit_before>package app_files_test

import (
	"archive/zip"
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"

	. "github.com/cloudfoundry/cli/cf/app_files"
	"github.com/cloudfoundry/gofileutils/fileutils"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

func readFile(file *os.File) []byte {
	bytes, err := ioutil.ReadAll(file)
	Expect(err).NotTo(HaveOccurred())
	return bytes
}

// Thanks to Svett Ralchev
// http://blog.ralch.com/tutorial/golang-working-with-zip/
func zipit(source, target string) error {
	zipfile, err := os.Create(target)
	if err != nil {
		return err
	}
	defer zipfile.Close()

	archive := zip.NewWriter(zipfile)
	defer archive.Close()

	info, err := os.Stat(source)
	if err != nil {
		return nil
	}

	var baseDir string
	if info.IsDir() {
		baseDir = filepath.Base(source)
	}

	err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		header, err := zip.FileInfoHeader(info)
		if err != nil {
			return err
		}

		if baseDir != "" {
			header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
		}

		if info.IsDir() {
			header.Name += string(os.PathSeparator)
		} else {
			header.Method = zip.Deflate
		}

		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close()
		_, err = io.Copy(writer, file)
		return err
	})

	return err
}

var _ = Describe("Zipper", func() {
	var filesInZip = []string{
		"foo.txt",
		"fooDir/",
		"fooDir/bar/",
		"lastDir/",
		"subDir/",
		"subDir/bar.txt",
		"subDir/otherDir/",
		"subDir/otherDir/file.txt",
	}

	It("zips directories", func() {
		fileutils.TempFile("zip_test", func(zipFile *os.File, err error) {
			workingDir, err := os.Getwd()
			Expect(err).NotTo(HaveOccurred())

			dir := filepath.Join(workingDir, "../../fixtures/zip/")
			err = os.Chmod(filepath.Join(dir, "subDir/bar.txt"), 0666)
			Expect(err).NotTo(HaveOccurred())

			zipper := ApplicationZipper{}
			err = zipper.Zip(dir, zipFile)
			Expect(err).NotTo(HaveOccurred())

			fileStat, err := zipFile.Stat()
			Expect(err).NotTo(HaveOccurred())

			reader, err := zip.NewReader(zipFile, fileStat.Size())
			Expect(err).NotTo(HaveOccurred())

			filenames := []string{}
			for _, file := range reader.File {
				filenames = append(filenames, file.Name)
			}

			Expect(filenames).To(Equal(filesInZip))

			readFileInZip := func(index int) (string, string) {
				buf := &bytes.Buffer{}
				file := reader.File[index]
				fReader, err := file.Open()
				_, err = io.Copy(buf, fReader)

				Expect(err).NotTo(HaveOccurred())

				return file.Name, string(buf.Bytes())
			}

			Expect(err).NotTo(HaveOccurred())

			name, contents := readFileInZip(0)
			Expect(name).To(Equal("foo.txt"))
			Expect(contents).To(Equal("This is a simple text file."))

			name, contents = readFileInZip(5)
			Expect(name).To(Equal("subDir/bar.txt"))
			Expect(contents).To(Equal("I am in a subdirectory."))
			Expect(reader.File[5].FileInfo().Mode()).To(Equal(os.FileMode(0666)))
		})
	})

	It("is a no-op for a zipfile", func() {
		fileutils.TempFile("zip_test", func(zipFile *os.File, err error) {
			dir, err := os.Getwd()
			Expect(err).NotTo(HaveOccurred())

			zipper := ApplicationZipper{}
			fixture := filepath.Join(dir, "../../fixtures/applications/example-app.zip")
			err = zipper.Zip(fixture, zipFile)
			Expect(err).NotTo(HaveOccurred())

			zippedFile, err := os.Open(fixture)
			Expect(err).NotTo(HaveOccurred())
			Expect(readFile(zipFile)).To(Equal(readFile(zippedFile)))
		})
	})

	It("returns an error when zipping fails", func() {
		fileutils.TempFile("zip_test", func(zipFile *os.File, err error) {
			zipper := ApplicationZipper{}
			err = zipper.Zip("/a/bogus/directory", zipFile)
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(ContainSubstring("open /a/bogus/directory"))
		})
	})

	It("returns an error when the directory is empty", func() {
		fileutils.TempFile("zip_test", func(zipFile *os.File, err error) {
			fileutils.TempDir("zip_test", func(emptyDir string, err error) {
				zipper := ApplicationZipper{}
				err = zipper.Zip(emptyDir, zipFile)
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("is empty"))
			})
		})
	})

	Describe(".Unzip", func() {
		Context("when the zipfile has an empty directory", func() {
			var (
				inDir, outDir, destDir string
				zipper                 ApplicationZipper
			)

			BeforeEach(func() {
				var err error
				inDir, err = ioutil.TempDir("", "zipper-unzip-in")
				Expect(err).NotTo(HaveOccurred())

				err = ioutil.WriteFile(path.Join(inDir, "file1"), []byte("file-1-contents"), 0664)
				Expect(err).NotTo(HaveOccurred())

				err = os.MkdirAll(path.Join(inDir, "dir1"), os.ModeDir|os.ModePerm)
				Expect(err).NotTo(HaveOccurred())

				err = ioutil.WriteFile(path.Join(inDir, "dir1", "file2"), []byte("file-2-contents"), 0644)
				Expect(err).NotTo(HaveOccurred())

				err = os.MkdirAll(path.Join(inDir, "dir2"), os.ModeDir|os.ModePerm)
				Expect(err).NotTo(HaveOccurred())

				outDir, err = ioutil.TempDir("", "zipper-unzip-out")
				Expect(err).NotTo(HaveOccurred())

				err = zipit(inDir, path.Join(outDir, "out.zip"))
				Expect(err).NotTo(HaveOccurred())

				destDir, err = ioutil.TempDir("", "dest-dir")
				Expect(err).NotTo(HaveOccurred())

				zipper = ApplicationZipper{}
			})

			AfterEach(func() {
				os.RemoveAll(inDir)
				os.RemoveAll(outDir)
			})

			It("includes all entries from the zip file in the destination", func() {
				err := zipper.Unzip(path.Join(outDir, "out.zip"), destDir)
				Expect(err).NotTo(HaveOccurred())

				expected := []string{
					"file1",
					"dir1/",
					"dir1/file2",
					"dir2",
				}

				for _, f := range expected {
					_, err := os.Stat(filepath.Join(destDir, path.Base(inDir), f))
					Expect(err).NotTo(HaveOccurred())
				}
			})
		})
	})

	Describe(".GetZipSize", func() {
		var zipper = ApplicationZipper{}

		It("returns the size of the zip file", func() {
			dir, err := os.Getwd()
			Expect(err).NotTo(HaveOccurred())
			zipFile := filepath.Join(dir, "../../fixtures/applications/example-app.zip")

			file, err := os.Open(zipFile)
			Expect(err).NotTo(HaveOccurred())

			fileSize, err := zipper.GetZipSize(file)
			Expect(err).NotTo(HaveOccurred())
			Expect(fileSize).To(Equal(int64(1803)))
		})

		It("returns  an error if the zip file cannot be found", func() {
			tmpFile, _ := os.Open("fooBar")
			_, sizeErr := zipper.GetZipSize(tmpFile)
			Expect(sizeErr).To(HaveOccurred())
		})
	})

})
<commit_msg>Fix test of ApplicationZipper Unzip()<commit_after>package app_files_test

import (
	"archive/zip"
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"

	. "github.com/cloudfoundry/cli/cf/app_files"
	"github.com/cloudfoundry/gofileutils/fileutils"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

func readFile(file *os.File) []byte {
	bytes, err := ioutil.ReadAll(file)
	Expect(err).NotTo(HaveOccurred())
	return bytes
}

// Thanks to Svett Ralchev
// http://blog.ralch.com/tutorial/golang-working-with-zip/
func zipit(source, target string) error {
	zipfile, err := os.Create(target)
	if err != nil {
		return err
	}
	defer zipfile.Close()

	archive := zip.NewWriter(zipfile)
	defer archive.Close()

	err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		header, err := zip.FileInfoHeader(info)
		if err != nil {
			return err
		}

		header.Name = strings.TrimPrefix(path, source)

		if info.IsDir() {
			header.Name += string(os.PathSeparator)
		} else {
			header.Method = zip.Deflate
		}

		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close()
		_, err = io.Copy(writer, file)
		return err
	})

	return err
}

var _ = Describe("Zipper", func() {
	var filesInZip = []string{
		"foo.txt",
		"fooDir/",
		"fooDir/bar/",
		"lastDir/",
		"subDir/",
		"subDir/bar.txt",
		"subDir/otherDir/",
		"subDir/otherDir/file.txt",
	}

	It("zips directories", func() {
		fileutils.TempFile("zip_test", func(zipFile *os.File, err error) {
			workingDir, err := os.Getwd()
			Expect(err).NotTo(HaveOccurred())

			dir := filepath.Join(workingDir, "../../fixtures/zip/")
			err = os.Chmod(filepath.Join(dir, "subDir/bar.txt"), 0666)
			Expect(err).NotTo(HaveOccurred())

			zipper := ApplicationZipper{}
			err = zipper.Zip(dir, zipFile)
			Expect(err).NotTo(HaveOccurred())

			fileStat, err := zipFile.Stat()
			Expect(err).NotTo(HaveOccurred())

			reader, err := zip.NewReader(zipFile, fileStat.Size())
			Expect(err).NotTo(HaveOccurred())

			filenames := []string{}
			for _, file := range reader.File {
				filenames = append(filenames, file.Name)
			}

			Expect(filenames).To(Equal(filesInZip))

			readFileInZip := func(index int) (string, string) {
				buf := &bytes.Buffer{}
				file := reader.File[index]
				fReader, err := file.Open()
				_, err = io.Copy(buf, fReader)

				Expect(err).NotTo(HaveOccurred())

				return file.Name, string(buf.Bytes())
			}

			Expect(err).NotTo(HaveOccurred())

			name, contents := readFileInZip(0)
			Expect(name).To(Equal("foo.txt"))
			Expect(contents).To(Equal("This is a simple text file."))

			name, contents = readFileInZip(5)
			Expect(name).To(Equal("subDir/bar.txt"))
			Expect(contents).To(Equal("I am in a subdirectory."))
			Expect(reader.File[5].FileInfo().Mode()).To(Equal(os.FileMode(0666)))
		})
	})

	It("is a no-op for a zipfile", func() {
		fileutils.TempFile("zip_test", func(zipFile *os.File, err error) {
			dir, err := os.Getwd()
			Expect(err).NotTo(HaveOccurred())

			zipper := ApplicationZipper{}
			fixture := filepath.Join(dir, "../../fixtures/applications/example-app.zip")
			err = zipper.Zip(fixture, zipFile)
			Expect(err).NotTo(HaveOccurred())

			zippedFile, err := os.Open(fixture)
			Expect(err).NotTo(HaveOccurred())
			Expect(readFile(zipFile)).To(Equal(readFile(zippedFile)))
		})
	})

	It("returns an error when zipping fails", func() {
		fileutils.TempFile("zip_test", func(zipFile *os.File, err error) {
			zipper := ApplicationZipper{}
			err = zipper.Zip("/a/bogus/directory", zipFile)
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(ContainSubstring("open /a/bogus/directory"))
		})
	})

	It("returns an error when the directory is empty", func() {
		fileutils.TempFile("zip_test", func(zipFile *os.File, err error) {
			fileutils.TempDir("zip_test", func(emptyDir string, err error) {
				zipper := ApplicationZipper{}
				err = zipper.Zip(emptyDir, zipFile)
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("is empty"))
			})
		})
	})

	Describe(".Unzip", func() {
		Context("when the zipfile has an empty directory", func() {
			var (
				inDir, outDir string
				zipper        ApplicationZipper
			)

			BeforeEach(func() {
				var err error
				inDir, err = ioutil.TempDir("", "zipper-unzip-in")
				Expect(err).NotTo(HaveOccurred())

				err = ioutil.WriteFile(path.Join(inDir, "file1"), []byte("file-1-contents"), 0664)
				Expect(err).NotTo(HaveOccurred())

				err = os.MkdirAll(path.Join(inDir, "dir1"), os.ModeDir|os.ModePerm)
				Expect(err).NotTo(HaveOccurred())

				err = ioutil.WriteFile(path.Join(inDir, "dir1", "file2"), []byte("file-2-contents"), 0644)
				Expect(err).NotTo(HaveOccurred())

				err = os.MkdirAll(path.Join(inDir, "dir2"), os.ModeDir|os.ModePerm)
				Expect(err).NotTo(HaveOccurred())

				outDir, err = ioutil.TempDir("", "zipper-unzip-out")
				Expect(err).NotTo(HaveOccurred())

				err = zipit(path.Join(inDir, "/"), path.Join(outDir, "out.zip"))
				Expect(err).NotTo(HaveOccurred())

				zipper = ApplicationZipper{}
			})

			AfterEach(func() {
				os.RemoveAll(inDir)
				os.RemoveAll(outDir)
			})

			It("includes all entries from the zip file in the destination", func() {
				destDir, err := ioutil.TempDir("", "dest-dir")
				Expect(err).NotTo(HaveOccurred())

				defer os.RemoveAll(destDir)

				err = zipper.Unzip(path.Join(outDir, "out.zip"), destDir)
				Expect(err).NotTo(HaveOccurred())

				expected := []string{
					"file1",
					"dir1/",
					"dir1/file2",
					"dir2",
				}

				for _, f := range expected {
					_, err := os.Stat(filepath.Join(destDir, f))
					Expect(err).NotTo(HaveOccurred())
				}
			})
		})
	})

	Describe(".GetZipSize", func() {
		var zipper = ApplicationZipper{}

		It("returns the size of the zip file", func() {
			dir, err := os.Getwd()
			Expect(err).NotTo(HaveOccurred())
			zipFile := filepath.Join(dir, "../../fixtures/applications/example-app.zip")

			file, err := os.Open(zipFile)
			Expect(err).NotTo(HaveOccurred())

			fileSize, err := zipper.GetZipSize(file)
			Expect(err).NotTo(HaveOccurred())
			Expect(fileSize).To(Equal(int64(1803)))
		})

		It("returns  an error if the zip file cannot be found", func() {
			tmpFile, _ := os.Open("fooBar")
			_, sizeErr := zipper.GetZipSize(tmpFile)
			Expect(sizeErr).To(HaveOccurred())
		})
	})

})
<|endoftext|>


<commit_before>package dht

// get_peers and announce_peers.

import (
	"context"
	"fmt"
	"sync/atomic"

	"github.com/anacrolix/log"
	"github.com/anacrolix/stm"
	"github.com/anacrolix/stm/stmutil"

	"github.com/anacrolix/dht/v2/int160"
	"github.com/anacrolix/dht/v2/krpc"
)

// Maintains state for an ongoing Announce operation. An Announce is started by calling
// Server.Announce.
type Announce struct {
	Peers chan PeersValues

	values chan PeersValues // Responses are pushed to this channel.

	// These only exist to support routines relying on channels for synchronization.
	done   <-chan struct{}
	cancel func()

	server   *Server
	infoHash int160.T // Target
	// The torrent port that we're announcing.
	announcePort int
	// The torrent port should be determined by the receiver in case we're
	// being NATed.
	announcePortImplied bool
	scrape              bool

	// List of pendingAnnouncePeer. TODO: Perhaps this should be sorted by distance to the target,
	// so we can do that sloppy hash stuff ;).
	pendingAnnouncePeers *stm.Var

	traversal traversal
}

func (a *Announce) String() string {
	return fmt.Sprintf("%[1]T %[1]p of %v on %v", a, a.infoHash, a.server)
}

// Returns the number of distinct remote addresses the announce has queried.
func (a *Announce) NumContacted() int64 {
	return atomic.LoadInt64(&a.traversal.stats.NumAddrsTried)
}

type AnnounceOpt *struct{}

var scrape = AnnounceOpt(&struct{}{})

func Scrape() AnnounceOpt { return scrape }

// Traverses the DHT graph toward nodes that store peers for the infohash, streaming them to the
// caller, and announcing the local node to each responding node if port is non-zero or impliedPort
// is true.
func (s *Server) Announce(infoHash [20]byte, port int, impliedPort bool, opts ...AnnounceOpt) (*Announce, error) {
	infoHashInt160 := int160.FromByteArray(infoHash)
	traversal, err := s.newTraversal(infoHashInt160)
	if err != nil {
		return nil, err
	}
	traversal.reason = "dht announce get_peers"
	a := &Announce{
		Peers:                make(chan PeersValues),
		values:               make(chan PeersValues),
		server:               s,
		infoHash:             infoHashInt160,
		announcePort:         port,
		announcePortImplied:  impliedPort,
		pendingAnnouncePeers: stm.NewVar(newPendingAnnouncePeers(infoHashInt160)),
		traversal:            traversal,
	}
	a.traversal.query = a.getPeers
	a.traversal.stopTraversal = a.stopTraversal
	for _, opt := range opts {
		if opt == scrape {
			a.scrape = true
		}
	}
	var ctx context.Context
	ctx, a.cancel = context.WithCancel(context.Background())
	a.done = ctx.Done()
	a.traversal.doneVar, _ = stmutil.ContextDoneVar(ctx)
	// Function ferries from values to Peers until discovery is halted.
	go func() {
		defer close(a.Peers)
		for {
			select {
			case psv := <-a.values:
				select {
				case a.Peers <- psv:
				case <-a.done:
					return
				}
			case <-a.done:
				return
			}
		}
	}()
	go a.run()
	return a, nil
}

// Store a potential peer announce. I think it's okay to have no peer ID here, as any next contact
// candidate will be closer than the "farthest" potential announce peer.
func (a *Announce) maybeAnnouncePeer(to Addr, token *string, peerId *krpc.ID) {
	if token == nil {
		return
	}
	if !a.server.config.NoSecurity && (peerId == nil || !NodeIdSecure(*peerId, to.IP())) {
		return
	}
	x := pendingAnnouncePeer{
		token: *token,
	}
	x.Addr = to.KRPC()
	if peerId != nil {
		id := int160.FromByteArray(*peerId)
		x.Id = &id
	}
	stm.AtomicModify(a.pendingAnnouncePeers, func(v pendingAnnouncePeers) pendingAnnouncePeers {
		return v.Push(x)
	})
}

func (a *Announce) announcePeer(peer pendingAnnouncePeer) numWrites {
	res := a.server.announcePeer(NewAddr(peer.Addr.UDP()), a.infoHash, a.announcePort, peer.token, a.announcePortImplied,
		QueryRateLimiting{NotFirst: true})
	return res.writes
}

func (a *Announce) beginAnnouncePeer(tx *stm.Tx) interface{} {
	tx.Assert(a.getPendingAnnouncePeers(tx).Len() != 0)
	new, x := tx.Get(a.pendingAnnouncePeers).(pendingAnnouncePeers).Pop(tx)
	tx.Set(a.pendingAnnouncePeers, new)

	return a.traversal.beginQuery(NewAddr(x.Addr.UDP()), "dht announce announce_peer", func() numWrites {
		a.server.logger().Printf("announce_peer to %v", x)
		return a.announcePeer(x)
	})(tx).(func())
}

func (a *Announce) getPeers(addr Addr) QueryResult {
	res := a.server.GetPeers(context.TODO(), addr, a.infoHash, a.scrape, QueryRateLimiting{
		// This is paid for in earlier in a call to Server.beginQuery.
		NotFirst: true,
	})
	m := res.Reply
	// Register suggested nodes closer to the target info-hash.
	if r := m.R; r != nil {
		select {
		case a.values <- PeersValues{
			Peers: r.Values,
			NodeInfo: krpc.NodeInfo{
				Addr: addr.KRPC(),
				ID:   r.ID,
			},
			Return: *r,
		}:
		case <-a.done:
		}
		// TODO: We're not distinguishing here for missing IDs. Those would be zero values?
		a.maybeAnnouncePeer(addr, r.Token, &r.ID)
	}
	return res
}

// Corresponds to the "values" key in a get_peers KRPC response. A list of
// peers that a node has reported as being in the swarm for a queried info
// hash.
type PeersValues struct {
	Peers         []Peer // Peers given in get_peers response.
	krpc.NodeInfo        // The node that gave the response.
	krpc.Return
}

// Stop the announce.
func (a *Announce) Close() {
	a.close()
}

func (a *Announce) close() {
	a.cancel()
}

func (a *Announce) farthestAnnouncePeer(tx *stm.Tx) (pendingAnnouncePeer, bool) {
	pending := a.getPendingAnnouncePeers(tx)
	if pending.Len() < pending.k {
		return pendingAnnouncePeer{}, false
	} else {
		return pending.Farthest()
	}
}

func (a *Announce) getPendingAnnouncePeers(tx *stm.Tx) pendingAnnouncePeers {
	return tx.Get(a.pendingAnnouncePeers).(pendingAnnouncePeers)
}

func (a *Announce) stopTraversal(tx *stm.Tx, next addrMaybeId) bool {
	farthest, ok := a.farthestAnnouncePeer(tx)
	return ok && farthest.closerThan(next, a.infoHash)
}

func (a *Announce) run() {
	defer a.cancel()
	a.traversal.run()
	a.logger().Printf("finishing get peers step")
	for {
		txRes := stm.Atomically(stm.Select(
			wrapRun(a.beginAnnouncePeer),
			func(tx *stm.Tx) interface{} {
				if tx.Get(a.traversal.doneVar).(bool) || a.traversal.getPending(tx) == 0 && a.getPendingAnnouncePeers(tx).Len() == 0 {
					return txResT{done: true}
				}
				return tx.Retry()
			},
		)).(txResT)
		if txRes.done {
			break
		}
		go txRes.run()
	}
}

func (a *Announce) logger() log.Logger {
	return a.server.logger()
}
<commit_msg>Move traversal to start of Announce struct<commit_after>package dht

// get_peers and announce_peers.

import (
	"context"
	"fmt"
	"sync/atomic"

	"github.com/anacrolix/log"
	"github.com/anacrolix/stm"
	"github.com/anacrolix/stm/stmutil"

	"github.com/anacrolix/dht/v2/int160"
	"github.com/anacrolix/dht/v2/krpc"
)

// Maintains state for an ongoing Announce operation. An Announce is started by calling
// Server.Announce.
type Announce struct {
	traversal traversal

	Peers chan PeersValues

	values chan PeersValues // Responses are pushed to this channel.

	// These only exist to support routines relying on channels for synchronization.
	done   <-chan struct{}
	cancel func()

	server   *Server
	infoHash int160.T // Target
	// The torrent port that we're announcing.
	announcePort int
	// The torrent port should be determined by the receiver in case we're
	// being NATed.
	announcePortImplied bool
	scrape              bool

	// List of pendingAnnouncePeer. TODO: Perhaps this should be sorted by distance to the target,
	// so we can do that sloppy hash stuff ;).
	pendingAnnouncePeers *stm.Var
}

func (a *Announce) String() string {
	return fmt.Sprintf("%[1]T %[1]p of %v on %v", a, a.infoHash, a.server)
}

// Returns the number of distinct remote addresses the announce has queried.
func (a *Announce) NumContacted() int64 {
	return atomic.LoadInt64(&a.traversal.stats.NumAddrsTried)
}

type AnnounceOpt *struct{}

var scrape = AnnounceOpt(&struct{}{})

func Scrape() AnnounceOpt { return scrape }

// Traverses the DHT graph toward nodes that store peers for the infohash, streaming them to the
// caller, and announcing the local node to each responding node if port is non-zero or impliedPort
// is true.
func (s *Server) Announce(infoHash [20]byte, port int, impliedPort bool, opts ...AnnounceOpt) (*Announce, error) {
	infoHashInt160 := int160.FromByteArray(infoHash)
	traversal, err := s.newTraversal(infoHashInt160)
	if err != nil {
		return nil, err
	}
	traversal.reason = "dht announce get_peers"
	a := &Announce{
		Peers:                make(chan PeersValues),
		values:               make(chan PeersValues),
		server:               s,
		infoHash:             infoHashInt160,
		announcePort:         port,
		announcePortImplied:  impliedPort,
		pendingAnnouncePeers: stm.NewVar(newPendingAnnouncePeers(infoHashInt160)),
		traversal:            traversal,
	}
	a.traversal.query = a.getPeers
	a.traversal.stopTraversal = a.stopTraversal
	for _, opt := range opts {
		if opt == scrape {
			a.scrape = true
		}
	}
	var ctx context.Context
	ctx, a.cancel = context.WithCancel(context.Background())
	a.done = ctx.Done()
	a.traversal.doneVar, _ = stmutil.ContextDoneVar(ctx)
	// Function ferries from values to Peers until discovery is halted.
	go func() {
		defer close(a.Peers)
		for {
			select {
			case psv := <-a.values:
				select {
				case a.Peers <- psv:
				case <-a.done:
					return
				}
			case <-a.done:
				return
			}
		}
	}()
	go a.run()
	return a, nil
}

// Store a potential peer announce. I think it's okay to have no peer ID here, as any next contact
// candidate will be closer than the "farthest" potential announce peer.
func (a *Announce) maybeAnnouncePeer(to Addr, token *string, peerId *krpc.ID) {
	if token == nil {
		return
	}
	if !a.server.config.NoSecurity && (peerId == nil || !NodeIdSecure(*peerId, to.IP())) {
		return
	}
	x := pendingAnnouncePeer{
		token: *token,
	}
	x.Addr = to.KRPC()
	if peerId != nil {
		id := int160.FromByteArray(*peerId)
		x.Id = &id
	}
	stm.AtomicModify(a.pendingAnnouncePeers, func(v pendingAnnouncePeers) pendingAnnouncePeers {
		return v.Push(x)
	})
}

func (a *Announce) announcePeer(peer pendingAnnouncePeer) numWrites {
	res := a.server.announcePeer(NewAddr(peer.Addr.UDP()), a.infoHash, a.announcePort, peer.token, a.announcePortImplied,
		QueryRateLimiting{NotFirst: true})
	return res.writes
}

func (a *Announce) beginAnnouncePeer(tx *stm.Tx) interface{} {
	tx.Assert(a.getPendingAnnouncePeers(tx).Len() != 0)
	new, x := tx.Get(a.pendingAnnouncePeers).(pendingAnnouncePeers).Pop(tx)
	tx.Set(a.pendingAnnouncePeers, new)

	return a.traversal.beginQuery(NewAddr(x.Addr.UDP()), "dht announce announce_peer", func() numWrites {
		a.server.logger().Printf("announce_peer to %v", x)
		return a.announcePeer(x)
	})(tx).(func())
}

func (a *Announce) getPeers(addr Addr) QueryResult {
	res := a.server.GetPeers(context.TODO(), addr, a.infoHash, a.scrape, QueryRateLimiting{
		// This is paid for in earlier in a call to Server.beginQuery.
		NotFirst: true,
	})
	m := res.Reply
	// Register suggested nodes closer to the target info-hash.
	if r := m.R; r != nil {
		select {
		case a.values <- PeersValues{
			Peers: r.Values,
			NodeInfo: krpc.NodeInfo{
				Addr: addr.KRPC(),
				ID:   r.ID,
			},
			Return: *r,
		}:
		case <-a.done:
		}
		// TODO: We're not distinguishing here for missing IDs. Those would be zero values?
		a.maybeAnnouncePeer(addr, r.Token, &r.ID)
	}
	return res
}

// Corresponds to the "values" key in a get_peers KRPC response. A list of
// peers that a node has reported as being in the swarm for a queried info
// hash.
type PeersValues struct {
	Peers         []Peer // Peers given in get_peers response.
	krpc.NodeInfo        // The node that gave the response.
	krpc.Return
}

// Stop the announce.
func (a *Announce) Close() {
	a.close()
}

func (a *Announce) close() {
	a.cancel()
}

func (a *Announce) farthestAnnouncePeer(tx *stm.Tx) (pendingAnnouncePeer, bool) {
	pending := a.getPendingAnnouncePeers(tx)
	if pending.Len() < pending.k {
		return pendingAnnouncePeer{}, false
	} else {
		return pending.Farthest()
	}
}

func (a *Announce) getPendingAnnouncePeers(tx *stm.Tx) pendingAnnouncePeers {
	return tx.Get(a.pendingAnnouncePeers).(pendingAnnouncePeers)
}

func (a *Announce) stopTraversal(tx *stm.Tx, next addrMaybeId) bool {
	farthest, ok := a.farthestAnnouncePeer(tx)
	return ok && farthest.closerThan(next, a.infoHash)
}

func (a *Announce) run() {
	defer a.cancel()
	a.traversal.run()
	a.logger().Printf("finishing get peers step")
	for {
		txRes := stm.Atomically(stm.Select(
			wrapRun(a.beginAnnouncePeer),
			func(tx *stm.Tx) interface{} {
				if tx.Get(a.traversal.doneVar).(bool) || a.traversal.getPending(tx) == 0 && a.getPendingAnnouncePeers(tx).Len() == 0 {
					return txResT{done: true}
				}
				return tx.Retry()
			},
		)).(txResT)
		if txRes.done {
			break
		}
		go txRes.run()
	}
}

func (a *Announce) logger() log.Logger {
	return a.server.logger()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"os/exec"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
)

const (
	varnishstatExe = "varnishstat"
)

var (
	descCache  = make(map[string]*prometheus.Desc)
	mDescCache sync.RWMutex
)

func scrapeVarnish(ch chan<- prometheus.Metric) (*bytes.Buffer, error) {
	params := []string{"-j"}
	if VarnishVersion.Major >= 4 && VarnishVersion.Minor >= 1 {
		// timeout to not hang for a long time if instance is not found.
		// Varnish 3.x exits immediately on faulty params
		params = append(params, "-t", "2")
	}
	if !StartParams.Params.isEmpty() {
		params = append(params, StartParams.Params.make()...)
	}
	buf, errExec := executeVarnishstat(params...)
	if errExec != nil {
		return buf, errExec
	}
	// The output JSON annoyingly is not stuctured so that we could make a nice map[string]struct for it.
	metricsJSON := make(map[string]interface{})
	dec := json.NewDecoder(buf)
	if err := dec.Decode(&metricsJSON); err != nil {
		return buf, err
	}

	// This is a bit broad but better than locking on each desc query below.
	mDescCache.Lock()
	defer mDescCache.Unlock()

	for vName, raw := range metricsJSON {
		if vName == "timestamp" {
			continue
		}
		if dt := reflect.TypeOf(raw); dt.Kind() != reflect.Map {
			if StartParams.Verbose {
				logWarn("Found unexpected data from json: %s: %#v", vName, raw)
			}
			continue
		}
		data, ok := raw.(map[string]interface{})
		if !ok {
			if StartParams.Verbose {
				logWarn("Failed to cast to map[string]interface{}: %s: %#v", vName, raw)
			}
			continue
		}
		var (
			vGroup       = prometheusGroup(vName)
			vDescription string
			vIdentifier  string
			vValue       float64
			vErr         error
		)
		if value, ok := data["description"]; ok && vErr == nil {
			if vDescription, ok = value.(string); !ok {
				vErr = fmt.Errorf("%s description it not a string", vName)
			}
		}
		if value, ok := data["ident"]; ok && vErr == nil {
			if vIdentifier, ok = value.(string); !ok {
				vErr = fmt.Errorf("%s ident it not a string", vName)
			}
		}
		if value, ok := data["value"]; ok && vErr == nil {
			if vValue, ok = value.(float64); !ok {
				vErr = fmt.Errorf("%s value it not a float64", vName)
			}
		}
		if vErr != nil {
			if StartParams.Verbose {
				logWarn(vErr.Error())
			}
			continue
		}

		pName, pDescription, pLabelKeys, pLabelValues := computePrometheusInfo(vName, vGroup, vIdentifier, vDescription)

		descKey := pName + "_" + strings.Join(pLabelKeys, "_")
		pDesc, ok := descCache[descKey]
		if !ok {
			pDesc = prometheus.NewDesc(
				pName,
				pDescription,
				pLabelKeys,
				nil,
			)
			descCache[descKey] = pDesc
		}
		ch <- prometheus.MustNewConstMetric(pDesc, prometheus.GaugeValue, vValue, pLabelValues...)
	}
	return buf, nil
}

// Returns the result of 'varnishtat' with optional command line params.
func executeVarnishstat(params ...string) (*bytes.Buffer, error) {
	buf := bytes.Buffer{}
	cmd := exec.Command(varnishstatExe, params...)
	cmd.Stdout = &buf
	cmd.Stderr = &buf
	if err := cmd.Start(); err != nil {
		return &buf, err
	}
	if err := cmd.Wait(); err != nil {
		return &buf, err
	}
	return &buf, nil
}

// varnishVersion

type varnishVersion struct {
	Major    int
	Minor    int
	Patch    int
	Revision string
}

func NewVarnishVersion() *varnishVersion {
	return &varnishVersion{
		Major: -1, Minor: -1, Patch: -1,
	}
}

func (v *varnishVersion) Initialize() error {
	return v.queryVersion()
}

func (v *varnishVersion) queryVersion() error {
	buf, err := executeVarnishstat("-V")
	if err != nil {
		return err
	}
	scanner := bufio.NewScanner(buf)
	for scanner.Scan() {
		return v.parseVersion(scanner.Text())
	}
	return nil
}

func (v *varnishVersion) parseVersion(version string) error {
	r := regexp.MustCompile(`(\d)\.?(\d)?\.?(\d)?(?:.*revision\s(.*)\))?`)
	parts := r.FindStringSubmatch(version)
	if len(parts) > 1 {
		if err := v.set(parts[1:]); err != nil {
			return err
		}
	}
	if !v.isValid() {
		return fmt.Errorf("Failed to resolve version from %q", version)
	}
	return nil
}

func (v *varnishVersion) Labels() map[string]string {
	labels := make(map[string]string)
	if v.Major != -1 {
		labels["major"] = strconv.Itoa(v.Major)
	}
	if v.Minor != -1 {
		labels["minor"] = strconv.Itoa(v.Minor)
	}
	if v.Patch != -1 {
		labels["patch"] = strconv.Itoa(v.Patch)
	}
	if v.Revision != "" {
		labels["revision"] = v.Revision
	}
	labels["version"] = v.VersionString()
	return labels
}

func (v *varnishVersion) set(parts []string) error {
	for i, part := range parts {
		if len(part) == 0 {
			continue
		}
		if i == 3 {
			v.Revision = part
			break
		}
		num, err := strconv.Atoi(part)
		if err != nil {
			return err
		}
		switch i {
		case 0:
			v.Major = num
		case 1:
			v.Minor = num
		case 2:
			v.Patch = num
		}
	}
	return nil
}

func (v *varnishVersion) isValid() bool {
	return v.Major != -1
}

// Version string with numbers only, no revision.
func (v *varnishVersion) VersionString() string {
	parts := []string{}
	for _, num := range []int{v.Major, v.Minor, v.Patch} {
		if num != -1 {
			parts = append(parts, strconv.Itoa(num))
		}
	}
	return strings.Join(parts, ".")
}

// Full version string, including revision.
func (v *varnishVersion) String() string {
	version := v.VersionString()
	if v.Revision != "" {
		version += " " + v.Revision
	}
	return version
}
<commit_msg>correct spelling mistake<commit_after>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"os/exec"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
)

const (
	varnishstatExe = "varnishstat"
)

var (
	descCache  = make(map[string]*prometheus.Desc)
	mDescCache sync.RWMutex
)

func scrapeVarnish(ch chan<- prometheus.Metric) (*bytes.Buffer, error) {
	params := []string{"-j"}
	if VarnishVersion.Major >= 4 && VarnishVersion.Minor >= 1 {
		// timeout to not hang for a long time if instance is not found.
		// Varnish 3.x exits immediately on faulty params
		params = append(params, "-t", "2")
	}
	if !StartParams.Params.isEmpty() {
		params = append(params, StartParams.Params.make()...)
	}
	buf, errExec := executeVarnishstat(params...)
	if errExec != nil {
		return buf, errExec
	}
	// The output JSON annoyingly is not structured so that we could make a nice map[string]struct for it.
	metricsJSON := make(map[string]interface{})
	dec := json.NewDecoder(buf)
	if err := dec.Decode(&metricsJSON); err != nil {
		return buf, err
	}

	// This is a bit broad but better than locking on each desc query below.
	mDescCache.Lock()
	defer mDescCache.Unlock()

	for vName, raw := range metricsJSON {
		if vName == "timestamp" {
			continue
		}
		if dt := reflect.TypeOf(raw); dt.Kind() != reflect.Map {
			if StartParams.Verbose {
				logWarn("Found unexpected data from json: %s: %#v", vName, raw)
			}
			continue
		}
		data, ok := raw.(map[string]interface{})
		if !ok {
			if StartParams.Verbose {
				logWarn("Failed to cast to map[string]interface{}: %s: %#v", vName, raw)
			}
			continue
		}
		var (
			vGroup       = prometheusGroup(vName)
			vDescription string
			vIdentifier  string
			vValue       float64
			vErr         error
		)
		if value, ok := data["description"]; ok && vErr == nil {
			if vDescription, ok = value.(string); !ok {
				vErr = fmt.Errorf("%s description it not a string", vName)
			}
		}
		if value, ok := data["ident"]; ok && vErr == nil {
			if vIdentifier, ok = value.(string); !ok {
				vErr = fmt.Errorf("%s ident it not a string", vName)
			}
		}
		if value, ok := data["value"]; ok && vErr == nil {
			if vValue, ok = value.(float64); !ok {
				vErr = fmt.Errorf("%s value it not a float64", vName)
			}
		}
		if vErr != nil {
			if StartParams.Verbose {
				logWarn(vErr.Error())
			}
			continue
		}

		pName, pDescription, pLabelKeys, pLabelValues := computePrometheusInfo(vName, vGroup, vIdentifier, vDescription)

		descKey := pName + "_" + strings.Join(pLabelKeys, "_")
		pDesc, ok := descCache[descKey]
		if !ok {
			pDesc = prometheus.NewDesc(
				pName,
				pDescription,
				pLabelKeys,
				nil,
			)
			descCache[descKey] = pDesc
		}
		ch <- prometheus.MustNewConstMetric(pDesc, prometheus.GaugeValue, vValue, pLabelValues...)
	}
	return buf, nil
}

// Returns the result of 'varnishtat' with optional command line params.
func executeVarnishstat(params ...string) (*bytes.Buffer, error) {
	buf := bytes.Buffer{}
	cmd := exec.Command(varnishstatExe, params...)
	cmd.Stdout = &buf
	cmd.Stderr = &buf
	if err := cmd.Start(); err != nil {
		return &buf, err
	}
	if err := cmd.Wait(); err != nil {
		return &buf, err
	}
	return &buf, nil
}

// varnishVersion

type varnishVersion struct {
	Major    int
	Minor    int
	Patch    int
	Revision string
}

func NewVarnishVersion() *varnishVersion {
	return &varnishVersion{
		Major: -1, Minor: -1, Patch: -1,
	}
}

func (v *varnishVersion) Initialize() error {
	return v.queryVersion()
}

func (v *varnishVersion) queryVersion() error {
	buf, err := executeVarnishstat("-V")
	if err != nil {
		return err
	}
	scanner := bufio.NewScanner(buf)
	for scanner.Scan() {
		return v.parseVersion(scanner.Text())
	}
	return nil
}

func (v *varnishVersion) parseVersion(version string) error {
	r := regexp.MustCompile(`(\d)\.?(\d)?\.?(\d)?(?:.*revision\s(.*)\))?`)
	parts := r.FindStringSubmatch(version)
	if len(parts) > 1 {
		if err := v.set(parts[1:]); err != nil {
			return err
		}
	}
	if !v.isValid() {
		return fmt.Errorf("Failed to resolve version from %q", version)
	}
	return nil
}

func (v *varnishVersion) Labels() map[string]string {
	labels := make(map[string]string)
	if v.Major != -1 {
		labels["major"] = strconv.Itoa(v.Major)
	}
	if v.Minor != -1 {
		labels["minor"] = strconv.Itoa(v.Minor)
	}
	if v.Patch != -1 {
		labels["patch"] = strconv.Itoa(v.Patch)
	}
	if v.Revision != "" {
		labels["revision"] = v.Revision
	}
	labels["version"] = v.VersionString()
	return labels
}

func (v *varnishVersion) set(parts []string) error {
	for i, part := range parts {
		if len(part) == 0 {
			continue
		}
		if i == 3 {
			v.Revision = part
			break
		}
		num, err := strconv.Atoi(part)
		if err != nil {
			return err
		}
		switch i {
		case 0:
			v.Major = num
		case 1:
			v.Minor = num
		case 2:
			v.Patch = num
		}
	}
	return nil
}

func (v *varnishVersion) isValid() bool {
	return v.Major != -1
}

// Version string with numbers only, no revision.
func (v *varnishVersion) VersionString() string {
	parts := []string{}
	for _, num := range []int{v.Major, v.Minor, v.Patch} {
		if num != -1 {
			parts = append(parts, strconv.Itoa(num))
		}
	}
	return strings.Join(parts, ".")
}

// Full version string, including revision.
func (v *varnishVersion) String() string {
	version := v.VersionString()
	if v.Revision != "" {
		version += " " + v.Revision
	}
	return version
}
<|endoftext|>


<commit_before>package chaoskube

import (
	"testing"

	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/labels"
)

// TestNew tests that arguments are passed to the new instance correctly
func TestNew(t *testing.T) {
	client := fake.NewSimpleClientset()
	selector := labels.SelectorFromSet(labels.Set{"foo": "bar"})

	chaoskube := New(client, selector, false, 42)

	if chaoskube == nil {
		t.Errorf("expected Chaoskube but got nothing")
	}

	if chaoskube.Client != client {
		t.Errorf("expected %#v, got %#v", client, chaoskube.Client)
	}

	if chaoskube.Selector.String() != "foo=bar" {
		t.Errorf("expected %s, got %s", "foo=bar", chaoskube.Selector.String())
	}

	if chaoskube.DryRun != false {
		t.Errorf("expected %t, got %t", false, chaoskube.DryRun)
	}

	if chaoskube.Seed != 42 {
		t.Errorf("expected %d, got %d", 42, chaoskube.Seed)
	}
}

// TestCandidates tests the set of pods available for termination
func TestCandidates(t *testing.T) {
	chaoskube := setup(t, labels.Everything(), false, 0)

	pods, err := chaoskube.Candidates()
	if err != nil {
		t.Fatal(err)
	}

	validatePods(t, pods, []map[string]string{
		{"namespace": "default", "name": "foo"},
		{"namespace": "default", "name": "bar"},
	})
}

// TestCandidatesWithLabelSelector tests that the list of pods available for
// termination can be restricted by providing a label selector.
func TestCandidatesWithLabelSelector(t *testing.T) {
	selector, err := labels.Parse("app=foo")
	if err != nil {
		t.Fatal(err)
	}

	chaoskube := setup(t, selector, false, 0)

	pods, err := chaoskube.Candidates()
	if err != nil {
		t.Fatal(err)
	}

	validatePods(t, pods, []map[string]string{
		{"namespace": "default", "name": "foo"},
	})
}

// TestVictim tests that a pod is chosen from the candidates
func TestVictim(t *testing.T) {
	chaoskube := setup(t, labels.Everything(), false, 2000)

	victim, err := chaoskube.Victim()
	if err != nil {
		t.Fatal(err)
	}

	validatePod(t, victim, map[string]string{
		"namespace": "default", "name": "foo",
	})
}

// TestAnotherVictim tests that the chosen victim is different for another seed
func TestAnotherVictim(t *testing.T) {
	chaoskube := setup(t, labels.Everything(), false, 4000)

	victim, err := chaoskube.Victim()
	if err != nil {
		t.Fatal(err)
	}

	validatePod(t, victim, map[string]string{
		"namespace": "default", "name": "bar",
	})
}

// TestAnotherVictimRespectsLabelSelector tests that a pod chosen from the
// candidates respects the provided label selector
func TestAnotherVictimRespectsLabelSelector(t *testing.T) {
	selector, err := labels.Parse("app=foo")
	if err != nil {
		t.Fatal(err)
	}

	chaoskube := setup(t, selector, false, 4000)

	victim, err := chaoskube.Victim()
	if err != nil {
		t.Fatal(err)
	}

	validatePod(t, victim, map[string]string{
		"namespace": "default", "name": "foo",
	})
}

// TestVictimRespectsLabelSelector tests that label selector supports exclusion
func TestVictimRespectsLabelSelector(t *testing.T) {
	selector, err := labels.Parse("app!=foo")
	if err != nil {
		t.Fatal(err)
	}

	chaoskube := setup(t, selector, false, 2000)

	victim, err := chaoskube.Victim()
	if err != nil {
		t.Fatal(err)
	}

	validatePod(t, victim, map[string]string{
		"namespace": "default", "name": "bar",
	})
}

// TestDeletePod tests deleting a particular pod
func TestDeletePod(t *testing.T) {
	chaoskube := setup(t, labels.Everything(), false, 0)

	victim := newPod("default", "foo")

	if err := chaoskube.DeletePod(victim); err != nil {
		t.Fatal(err)
	}

	pods, err := chaoskube.Candidates()
	if err != nil {
		t.Fatal(err)
	}

	validatePods(t, pods, []map[string]string{
		{"namespace": "default", "name": "bar"},
	})
}

// TestDeletePodDryRun tests that enabled dry run doesn't delete the pod
func TestDeletePodDryRun(t *testing.T) {
	chaoskube := setup(t, labels.Everything(), true, 0)

	victim := newPod("default", "foo")

	if err := chaoskube.DeletePod(victim); err != nil {
		t.Fatal(err)
	}

	pods, err := chaoskube.Candidates()
	if err != nil {
		t.Fatal(err)
	}

	validatePods(t, pods, []map[string]string{
		{"namespace": "default", "name": "foo"},
		{"namespace": "default", "name": "bar"},
	})
}

// helper functions

func validatePods(t *testing.T, pods []v1.Pod, expected []map[string]string) {
	if len(pods) != len(expected) {
		t.Fatalf("expected %d pod(s), got %d", len(expected), len(pods))
	}

	for i, pod := range pods {
		validatePod(t, pod, expected[i])
	}
}

func validatePod(t *testing.T, pod v1.Pod, expected map[string]string) {
	if pod.Namespace != expected["namespace"] {
		t.Errorf("expected %s, got %s", expected["namespace"], pod.Namespace)
	}

	if pod.Name != expected["name"] {
		t.Errorf("expected %s, got %s", expected["name"], pod.Name)
	}
}

func newPod(namespace, name string) v1.Pod {
	pod := v1.Pod{
		ObjectMeta: v1.ObjectMeta{
			Namespace: namespace,
			Name:      name,
			Labels: map[string]string{
				"app": name,
			},
		},
	}

	return pod
}

func setup(t *testing.T, selector labels.Selector, dryRun bool, seed int64) *Chaoskube {
	pods := []v1.Pod{
		newPod("default", "foo"),
		newPod("default", "bar"),
	}

	client := fake.NewSimpleClientset()

	for _, pod := range pods {
		if _, err := client.Core().Pods(pod.Namespace).Create(&pod); err != nil {
			t.Fatal(err)
		}
	}

	return New(client, selector, dryRun, seed)
}
<commit_msg>ref: test excluding label selector on Canidates<commit_after>package chaoskube

import (
	"testing"

	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/labels"
)

// TestNew tests that arguments are passed to the new instance correctly
func TestNew(t *testing.T) {
	client := fake.NewSimpleClientset()
	selector := labels.SelectorFromSet(labels.Set{"foo": "bar"})

	chaoskube := New(client, selector, false, 42)

	if chaoskube == nil {
		t.Errorf("expected Chaoskube but got nothing")
	}

	if chaoskube.Client != client {
		t.Errorf("expected %#v, got %#v", client, chaoskube.Client)
	}

	if chaoskube.Selector.String() != "foo=bar" {
		t.Errorf("expected %s, got %s", "foo=bar", chaoskube.Selector.String())
	}

	if chaoskube.DryRun != false {
		t.Errorf("expected %t, got %t", false, chaoskube.DryRun)
	}

	if chaoskube.Seed != 42 {
		t.Errorf("expected %d, got %d", 42, chaoskube.Seed)
	}
}

// TestCandidates tests the set of pods available for termination
func TestCandidates(t *testing.T) {
	chaoskube := setup(t, labels.Everything(), false, 0)

	pods, err := chaoskube.Candidates()
	if err != nil {
		t.Fatal(err)
	}

	validatePods(t, pods, []map[string]string{
		{"namespace": "default", "name": "foo"},
		{"namespace": "default", "name": "bar"},
	})
}

// TestCandidatesLabelSelector tests that the list of pods available for
// termination can be restricted by providing a label selector.
func TestCandidatesLabelSelector(t *testing.T) {
	selector, err := labels.Parse("app=foo")
	if err != nil {
		t.Fatal(err)
	}

	chaoskube := setup(t, selector, false, 0)

	pods, err := chaoskube.Candidates()
	if err != nil {
		t.Fatal(err)
	}

	validatePods(t, pods, []map[string]string{
		{"namespace": "default", "name": "foo"},
	})
}

// TestCandidatesExcludingLabelSelector tests that label selector supports exclusion
func TestCandidatesExcludingLabelSelector(t *testing.T) {
	selector, err := labels.Parse("app!=foo")
	if err != nil {
		t.Fatal(err)
	}

	chaoskube := setup(t, selector, false, 0)

	pods, err := chaoskube.Candidates()
	if err != nil {
		t.Fatal(err)
	}

	validatePods(t, pods, []map[string]string{
		{"namespace": "default", "name": "bar"},
	})
}

// TestVictim tests that a pod is chosen from the candidates
func TestVictim(t *testing.T) {
	chaoskube := setup(t, labels.Everything(), false, 2000)

	victim, err := chaoskube.Victim()
	if err != nil {
		t.Fatal(err)
	}

	validatePod(t, victim, map[string]string{
		"namespace": "default", "name": "foo",
	})
}

// TestAnotherVictim tests that the chosen victim is different for another seed
func TestAnotherVictim(t *testing.T) {
	chaoskube := setup(t, labels.Everything(), false, 4000)

	victim, err := chaoskube.Victim()
	if err != nil {
		t.Fatal(err)
	}

	validatePod(t, victim, map[string]string{
		"namespace": "default", "name": "bar",
	})
}

// TestAnotherVictimRespectsLabelSelector tests that a pod chosen from the
// candidates respects the provided label selector
func TestAnotherVictimRespectsLabelSelector(t *testing.T) {
	selector, err := labels.Parse("app=foo")
	if err != nil {
		t.Fatal(err)
	}

	chaoskube := setup(t, selector, false, 4000)

	victim, err := chaoskube.Victim()
	if err != nil {
		t.Fatal(err)
	}

	validatePod(t, victim, map[string]string{
		"namespace": "default", "name": "foo",
	})
}

// TestDeletePod tests deleting a particular pod
func TestDeletePod(t *testing.T) {
	chaoskube := setup(t, labels.Everything(), false, 0)

	victim := newPod("default", "foo")

	if err := chaoskube.DeletePod(victim); err != nil {
		t.Fatal(err)
	}

	pods, err := chaoskube.Candidates()
	if err != nil {
		t.Fatal(err)
	}

	validatePods(t, pods, []map[string]string{
		{"namespace": "default", "name": "bar"},
	})
}

// TestDeletePodDryRun tests that enabled dry run doesn't delete the pod
func TestDeletePodDryRun(t *testing.T) {
	chaoskube := setup(t, labels.Everything(), true, 0)

	victim := newPod("default", "foo")

	if err := chaoskube.DeletePod(victim); err != nil {
		t.Fatal(err)
	}

	pods, err := chaoskube.Candidates()
	if err != nil {
		t.Fatal(err)
	}

	validatePods(t, pods, []map[string]string{
		{"namespace": "default", "name": "foo"},
		{"namespace": "default", "name": "bar"},
	})
}

// helper functions

func validatePods(t *testing.T, pods []v1.Pod, expected []map[string]string) {
	if len(pods) != len(expected) {
		t.Fatalf("expected %d pod(s), got %d", len(expected), len(pods))
	}

	for i, pod := range pods {
		validatePod(t, pod, expected[i])
	}
}

func validatePod(t *testing.T, pod v1.Pod, expected map[string]string) {
	if pod.Namespace != expected["namespace"] {
		t.Errorf("expected %s, got %s", expected["namespace"], pod.Namespace)
	}

	if pod.Name != expected["name"] {
		t.Errorf("expected %s, got %s", expected["name"], pod.Name)
	}
}

func newPod(namespace, name string) v1.Pod {
	pod := v1.Pod{
		ObjectMeta: v1.ObjectMeta{
			Namespace: namespace,
			Name:      name,
			Labels: map[string]string{
				"app": name,
			},
		},
	}

	return pod
}

func setup(t *testing.T, selector labels.Selector, dryRun bool, seed int64) *Chaoskube {
	pods := []v1.Pod{
		newPod("default", "foo"),
		newPod("default", "bar"),
	}

	client := fake.NewSimpleClientset()

	for _, pod := range pods {
		if _, err := client.Core().Pods(pod.Namespace).Create(&pod); err != nil {
			t.Fatal(err)
		}
	}

	return New(client, selector, dryRun, seed)
}
<|endoftext|>


<commit_before>// Copyright 2014 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import "fmt"

// Pages represents a GitHub Pages site configuration.
type Pages struct {
	URL       *string `json:"url,omitempty"`
	Status    *string `json:"status,omitempty"`
	CNAME     *string `json:"cname,omitempty"`
	Custom404 *bool   `json:"custom_404,omitempty"`
	HTMLURL   *string `json:"html_url,omitempty"`
}

// PagesError represents a build error for a GitHub Pages site.
type PagesError struct {
	Message *string `json:"message,omitempty"`
}

// PagesBuild represents the build information for a GitHub Pages site.
type PagesBuild struct {
	URL       *string     `json:"url,omitempty"`
	Status    *string     `json:"status,omitempty"`
	Error     *PagesError `json:"error,omitempty"`
	Pusher    *User       `json:"pusher,omitempty"`
	Commit    *string     `json:"commit,omitempty"`
	Duration  *int        `json:"duration,omitempty"`
	CreatedAt *Timestamp  `json:"created_at,omitempty"`
	UpdatedAt *Timestamp  `json:"created_at,omitempty"`
}

// GetPagesInfo fetches information about a GitHub Pages site.
//
// GitHub API docs: https://developer.github.com/v3/repos/pages/#get-information-about-a-pages-site
func (s *RepositoriesService) GetPagesInfo(owner, repo string) (*Pages, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/pages", owner, repo)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypePagesPreview)

	site := new(Pages)
	resp, err := s.client.Do(req, site)
	if err != nil {
		return nil, resp, err
	}

	return site, resp, err
}

// ListPagesBuilds lists the builds for a GitHub Pages site.
//
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-pages-builds
func (s *RepositoriesService) ListPagesBuilds(owner, repo string) ([]*PagesBuild, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var pages []*PagesBuild
	resp, err := s.client.Do(req, &pages)
	if err != nil {
		return nil, resp, err
	}

	return pages, resp, err
}

// GetLatestPagesBuild fetches the latest build information for a GitHub pages site.
//
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-latest-pages-build
func (s *RepositoriesService) GetLatestPagesBuild(owner, repo string) (*PagesBuild, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/pages/builds/latest", owner, repo)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	build := new(PagesBuild)
	resp, err := s.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// GetPageBuild fetches the specific build information for a GitHub pages site.
//
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-a-specific-pages-build
func (s *RepositoriesService) GetPageBuild(owner, repo string, id int) (*PagesBuild, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/pages/builds/%v", owner, repo, id)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	build := new(PagesBuild)
	resp, err := s.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// RequestPageBuild requests a build of a GitHub Pages site without needing to push new commit.
//
// GitHub API docs: https://developer.github.com/v3/repos/pages/#request-a-page-build
func (s *RepositoriesService) RequestPageBuild(owner, repo string) (*PagesBuild, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo)
	req, err := s.client.NewRequest("POST", u, nil)
	if err != nil {
		return nil, nil, err
	}

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypePagesPreview)

	build := new(PagesBuild)
	resp, err := s.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}
<commit_msg>changed updatedAt json to correct json value<commit_after>// Copyright 2014 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import "fmt"

// Pages represents a GitHub Pages site configuration.
type Pages struct {
	URL       *string `json:"url,omitempty"`
	Status    *string `json:"status,omitempty"`
	CNAME     *string `json:"cname,omitempty"`
	Custom404 *bool   `json:"custom_404,omitempty"`
	HTMLURL   *string `json:"html_url,omitempty"`
}

// PagesError represents a build error for a GitHub Pages site.
type PagesError struct {
	Message *string `json:"message,omitempty"`
}

// PagesBuild represents the build information for a GitHub Pages site.
type PagesBuild struct {
	URL       *string     `json:"url,omitempty"`
	Status    *string     `json:"status,omitempty"`
	Error     *PagesError `json:"error,omitempty"`
	Pusher    *User       `json:"pusher,omitempty"`
	Commit    *string     `json:"commit,omitempty"`
	Duration  *int        `json:"duration,omitempty"`
	CreatedAt *Timestamp  `json:"created_at,omitempty"`
	UpdatedAt *Timestamp  `json:"updated_at,omitempty"`
}

// GetPagesInfo fetches information about a GitHub Pages site.
//
// GitHub API docs: https://developer.github.com/v3/repos/pages/#get-information-about-a-pages-site
func (s *RepositoriesService) GetPagesInfo(owner, repo string) (*Pages, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/pages", owner, repo)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypePagesPreview)

	site := new(Pages)
	resp, err := s.client.Do(req, site)
	if err != nil {
		return nil, resp, err
	}

	return site, resp, err
}

// ListPagesBuilds lists the builds for a GitHub Pages site.
//
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-pages-builds
func (s *RepositoriesService) ListPagesBuilds(owner, repo string) ([]*PagesBuild, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var pages []*PagesBuild
	resp, err := s.client.Do(req, &pages)
	if err != nil {
		return nil, resp, err
	}

	return pages, resp, err
}

// GetLatestPagesBuild fetches the latest build information for a GitHub pages site.
//
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-latest-pages-build
func (s *RepositoriesService) GetLatestPagesBuild(owner, repo string) (*PagesBuild, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/pages/builds/latest", owner, repo)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	build := new(PagesBuild)
	resp, err := s.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// GetPageBuild fetches the specific build information for a GitHub pages site.
//
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-a-specific-pages-build
func (s *RepositoriesService) GetPageBuild(owner, repo string, id int) (*PagesBuild, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/pages/builds/%v", owner, repo, id)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	build := new(PagesBuild)
	resp, err := s.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// RequestPageBuild requests a build of a GitHub Pages site without needing to push new commit.
//
// GitHub API docs: https://developer.github.com/v3/repos/pages/#request-a-page-build
func (s *RepositoriesService) RequestPageBuild(owner, repo string) (*PagesBuild, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo)
	req, err := s.client.NewRequest("POST", u, nil)
	if err != nil {
		return nil, nil, err
	}

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypePagesPreview)

	build := new(PagesBuild)
	resp, err := s.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}
<|endoftext|>