<commit_before>// +build linux

package libcontainer

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/docker/libcontainer/configs"
	"github.com/docker/libcontainer/label"
)

const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV

var baseMounts = []*configs.Mount{
	{
		Source:      "proc",
		Destination: "/proc",
		Device:      "proc",
		Flags:       defaultMountFlags,
	},
	{
		Source:      "tmpfs",
		Destination: "/dev",
		Device:      "tmpfs",
		Flags:       syscall.MS_NOSUID | syscall.MS_STRICTATIME,
		Data:        "mode=755",
	},
	{
		Source:      "devpts",
		Destination: "/dev/pts",
		Device:      "devpts",
		Flags:       syscall.MS_NOSUID | syscall.MS_NOEXEC,
		Data:        "newinstance,ptmxmode=0666,mode=0620,gid=5",
	},
}

// setupRootfs sets up the devices, mount points, and filesystems for use inside a
// new mount namespace.
func setupRootfs(config *configs.Config, console *linuxConsole) (err error) {
	if err := prepareRoot(config); err != nil {
		return newSystemError(err)
	}
	for _, m := range append(baseMounts, config.Mounts...) {
		if err := mount(m, config.Rootfs, config.MountLabel); err != nil {
			return newSystemError(err)
		}
	}
	if err := createDevices(config); err != nil {
		return newSystemError(err)
	}
	if err := setupPtmx(config, console); err != nil {
		return newSystemError(err)
	}
	// stdin, stdout and stderr could be pointing to /dev/null from parent namespace.
	// re-open them inside this namespace.
	if err := reOpenDevNull(config.Rootfs); err != nil {
		return newSystemError(err)
	}
	if err := setupDevSymlinks(config.Rootfs); err != nil {
		return newSystemError(err)
	}
	if err := syscall.Chdir(config.Rootfs); err != nil {
		return newSystemError(err)
	}
	if config.NoPivotRoot {
		err = msMoveRoot(config.Rootfs)
	} else {
		err = pivotRoot(config.Rootfs, config.PivotDir)
	}
	if err != nil {
		return newSystemError(err)
	}
	if config.Readonlyfs {
		if err := setReadonly(); err != nil {
			return newSystemError(err)
		}
	}
	syscall.Umask(0022)
	return nil
}

func mount(m *configs.Mount, rootfs, mountLabel string) error {
	var (
		dest = m.Destination
		data = label.FormatMountLabel(m.Data, mountLabel)
	)
	if !strings.HasPrefix(dest, rootfs) {
		dest = filepath.Join(rootfs, dest)
	}

	switch m.Device {
	case "proc":
		if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) {
			return err
		}
		return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), "")
	case "tmpfs", "mqueue", "devpts", "sysfs":
		if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) {
			return err
		}
		return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data)
	case "bind":
		stat, err := os.Stat(m.Source)
		if err != nil {
			// error out if the source of a bind mount does not exist as we will be
			// unable to bind anything to it.
			return err
		}
		if err := createIfNotExists(dest, stat.IsDir()); err != nil {
			return err
		}
		if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data); err != nil {
			return err
		}
		if m.Flags&syscall.MS_RDONLY != 0 {
			if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags|syscall.MS_REMOUNT), ""); err != nil {
				return err
			}
		}
		if m.Relabel != "" {
			if err := label.Relabel(m.Source, mountLabel, m.Relabel); err != nil {
				return err
			}
		}
		if m.Flags&syscall.MS_PRIVATE != 0 {
			if err := syscall.Mount("", dest, "none", uintptr(syscall.MS_PRIVATE), ""); err != nil {
				return err
			}
		}
	default:
		return fmt.Errorf("unknown mount device %q to %q", m.Device, m.Destination)
	}
	return nil
}

func setupDevSymlinks(rootfs string) error {
	var links = [][2]string{
		{"/proc/self/fd", "/dev/fd"},
		{"/proc/self/fd/0", "/dev/stdin"},
		{"/proc/self/fd/1", "/dev/stdout"},
		{"/proc/self/fd/2", "/dev/stderr"},
	}
	// kcore support can be toggled with CONFIG_PROC_KCORE; only create a symlink
	// in /dev if it exists in /proc.
	if _, err := os.Stat("/proc/kcore"); err == nil {
		links = append(links, [2]string{"/proc/kcore", "/dev/kcore"})
	}
	for _, link := range links {
		var (
			src = link[0]
			dst = filepath.Join(rootfs, link[1])
		)
		if err := os.Symlink(src, dst); err != nil && !os.IsExist(err) {
			return fmt.Errorf("symlink %s %s %s", src, dst, err)
		}
	}
	return nil
}

// If stdin, stdout or stderr are pointing to '/dev/null' in the global mount namespace,
// this method will make them point to '/dev/null' in this namespace.
func reOpenDevNull(rootfs string) error {
	var stat, devNullStat syscall.Stat_t
	file, err := os.Open(filepath.Join(rootfs, "/dev/null"))
	if err != nil {
		return fmt.Errorf("Failed to open /dev/null - %s", err)
	}
	defer file.Close()
	if err := syscall.Fstat(int(file.Fd()), &devNullStat); err != nil {
		return err
	}
	for fd := 0; fd < 3; fd++ {
		if err := syscall.Fstat(fd, &stat); err != nil {
			return err
		}
		if stat.Rdev == devNullStat.Rdev {
			// Close and re-open the fd.
			if err := syscall.Dup2(int(file.Fd()), fd); err != nil {
				return err
			}
		}
	}
	return nil
}

// Create the device nodes in the container.
func createDevices(config *configs.Config) error {
	oldMask := syscall.Umask(0000)
	for _, node := range config.Devices {
		if err := createDeviceNode(config.Rootfs, node); err != nil {
			syscall.Umask(oldMask)
			return err
		}
	}
	syscall.Umask(oldMask)
	return nil
}

// Creates the device node in the rootfs of the container.
func createDeviceNode(rootfs string, node *configs.Device) error {
	dest := filepath.Join(rootfs, node.Path)
	if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
		return err
	}
	if err := mknodDevice(dest, node); err != nil {
		if os.IsExist(err) {
			return nil
		}
		if err != syscall.EPERM {
			return err
		}
		// containers running in a user namespace are not allowed to mknod
		// devices so we can just bind mount it from the host.
		f, err := os.Create(dest)
		if err != nil && !os.IsExist(err) {
			return err
		}
		if f != nil {
			f.Close()
		}
		return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "")
	}
	return nil
}

func mknodDevice(dest string, node *configs.Device) error {
	fileMode := node.FileMode
	switch node.Type {
	case 'c':
		fileMode |= syscall.S_IFCHR
	case 'b':
		fileMode |= syscall.S_IFBLK
	default:
		return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path)
	}
	if err := syscall.Mknod(dest, uint32(fileMode), node.Mkdev()); err != nil {
		return err
	}
	return syscall.Chown(dest, int(node.Uid), int(node.Gid))
}

func prepareRoot(config *configs.Config) error {
	flag := syscall.MS_PRIVATE | syscall.MS_REC
	if config.NoPivotRoot {
		flag = syscall.MS_SLAVE | syscall.MS_REC
	}
	if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil {
		return err
	}
	return syscall.Mount(config.Rootfs, config.Rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, "")
}

func setReadonly() error {
	return syscall.Mount("/", "/", "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, "")
}

func setupPtmx(config *configs.Config, console *linuxConsole) error {
	ptmx := filepath.Join(config.Rootfs, "dev/ptmx")
	if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) {
		return err
	}
	if err := os.Symlink("pts/ptmx", ptmx); err != nil {
		return fmt.Errorf("symlink dev ptmx %s", err)
	}
	if console != nil {
		return console.mount(config.Rootfs, config.MountLabel, 0, 0)
	}
	return nil
}

func pivotRoot(rootfs, pivotBaseDir string) error {
	if pivotBaseDir == "" {
		pivotBaseDir = "/"
	}
	tmpDir := filepath.Join(rootfs, pivotBaseDir)
	if err := os.MkdirAll(tmpDir, 0755); err != nil {
		return fmt.Errorf("can't create tmp dir %s, error %v", tmpDir, err)
	}
	pivotDir, err := ioutil.TempDir(tmpDir, ".pivot_root")
	if err != nil {
		return fmt.Errorf("can't create pivot_root dir %s, error %v", pivotDir, err)
	}
	if err := syscall.PivotRoot(rootfs, pivotDir); err != nil {
		return fmt.Errorf("pivot_root %s", err)
	}
	if err := syscall.Chdir("/"); err != nil {
		return fmt.Errorf("chdir / %s", err)
	}
	// path to pivot dir now changed, update
	pivotDir = filepath.Join(pivotBaseDir, filepath.Base(pivotDir))
	if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil {
		return fmt.Errorf("unmount pivot_root dir %s", err)
	}
	return os.Remove(pivotDir)
}

func msMoveRoot(rootfs string) error {
	if err := syscall.Mount(rootfs, "/", "", syscall.MS_MOVE, ""); err != nil {
		return err
	}
	if err := syscall.Chroot("."); err != nil {
		return err
	}
	return syscall.Chdir("/")
}

// createIfNotExists creates a file or a directory only if it does not already exist.
func createIfNotExists(path string, isDir bool) error {
	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			if isDir {
				return os.MkdirAll(path, 0755)
			}
			if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
				return err
			}
			f, err := os.OpenFile(path, os.O_CREATE, 0755)
			if err != nil {
				return err
			}
			f.Close()
		}
	}
	return nil
}

// remountReadonly will bind over the top of an existing path and ensure that it is read-only.
func remountReadonly(path string) error {
	for i := 0; i < 5; i++ {
		if err := syscall.Mount("", path, "", syscall.MS_REMOUNT|syscall.MS_RDONLY, ""); err != nil && !os.IsNotExist(err) {
			switch err {
			case syscall.EINVAL:
				// Probably not a mountpoint, use bind-mount
				if err := syscall.Mount(path, path, "", syscall.MS_BIND, ""); err != nil {
					return err
				}
				return syscall.Mount(path, path, "", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC|defaultMountFlags, "")
			case syscall.EBUSY:
				time.Sleep(100 * time.Millisecond)
				continue
			default:
				return err
			}
		}
		return nil
	}
	return fmt.Errorf("unable to mount %s as readonly max retries reached", path)
}

// maskFile bind mounts /dev/null over the top of the specified path inside a container
// to avoid security issues from processes reading information from non-namespace aware mounts ( proc/kcore ).
func maskFile(path string) error {
	if err := syscall.Mount("/dev/null", path, "", syscall.MS_BIND, ""); err != nil && !os.IsNotExist(err) {
		return err
	}
	return nil
}
<commit_msg>Don't label mqueue when mounting<commit_after>// +build linux

package libcontainer

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/docker/libcontainer/configs"
	"github.com/docker/libcontainer/label"
)

const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV

var baseMounts = []*configs.Mount{
	{
		Source:      "proc",
		Destination: "/proc",
		Device:      "proc",
		Flags:       defaultMountFlags,
	},
	{
		Source:      "tmpfs",
		Destination: "/dev",
		Device:      "tmpfs",
		Flags:       syscall.MS_NOSUID | syscall.MS_STRICTATIME,
		Data:        "mode=755",
	},
	{
		Source:      "devpts",
		Destination: "/dev/pts",
		Device:      "devpts",
		Flags:       syscall.MS_NOSUID | syscall.MS_NOEXEC,
		Data:        "newinstance,ptmxmode=0666,mode=0620,gid=5",
	},
}

// setupRootfs sets up the devices, mount points, and filesystems for use inside a
// new mount namespace.
func setupRootfs(config *configs.Config, console *linuxConsole) (err error) {
	if err := prepareRoot(config); err != nil {
		return newSystemError(err)
	}
	for _, m := range append(baseMounts, config.Mounts...) {
		if err := mount(m, config.Rootfs, config.MountLabel); err != nil {
			return newSystemError(err)
		}
	}
	if err := createDevices(config); err != nil {
		return newSystemError(err)
	}
	if err := setupPtmx(config, console); err != nil {
		return newSystemError(err)
	}
	// stdin, stdout and stderr could be pointing to /dev/null from parent namespace.
	// re-open them inside this namespace.
	if err := reOpenDevNull(config.Rootfs); err != nil {
		return newSystemError(err)
	}
	if err := setupDevSymlinks(config.Rootfs); err != nil {
		return newSystemError(err)
	}
	if err := syscall.Chdir(config.Rootfs); err != nil {
		return newSystemError(err)
	}
	if config.NoPivotRoot {
		err = msMoveRoot(config.Rootfs)
	} else {
		err = pivotRoot(config.Rootfs, config.PivotDir)
	}
	if err != nil {
		return newSystemError(err)
	}
	if config.Readonlyfs {
		if err := setReadonly(); err != nil {
			return newSystemError(err)
		}
	}
	syscall.Umask(0022)
	return nil
}

func mount(m *configs.Mount, rootfs, mountLabel string) error {
	var (
		dest = m.Destination
		data = label.FormatMountLabel(m.Data, mountLabel)
	)
	if !strings.HasPrefix(dest, rootfs) {
		dest = filepath.Join(rootfs, dest)
	}

	switch m.Device {
	case "proc":
		if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) {
			return err
		}
		return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), "")
	case "tmpfs", "mqueue", "devpts", "sysfs":
		if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) {
			return err
		}
		if m.Device == "mqueue" {
			// mqueue should not be labeled, otherwise the mount will fail
			data = ""
		}
		return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data)
	case "bind":
		stat, err := os.Stat(m.Source)
		if err != nil {
			// error out if the source of a bind mount does not exist as we will be
			// unable to bind anything to it.
			return err
		}
		if err := createIfNotExists(dest, stat.IsDir()); err != nil {
			return err
		}
		if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data); err != nil {
			return err
		}
		if m.Flags&syscall.MS_RDONLY != 0 {
			if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags|syscall.MS_REMOUNT), ""); err != nil {
				return err
			}
		}
		if m.Relabel != "" {
			if err := label.Relabel(m.Source, mountLabel, m.Relabel); err != nil {
				return err
			}
		}
		if m.Flags&syscall.MS_PRIVATE != 0 {
			if err := syscall.Mount("", dest, "none", uintptr(syscall.MS_PRIVATE), ""); err != nil {
				return err
			}
		}
	default:
		return fmt.Errorf("unknown mount device %q to %q", m.Device, m.Destination)
	}
	return nil
}

func setupDevSymlinks(rootfs string) error {
	var links = [][2]string{
		{"/proc/self/fd", "/dev/fd"},
		{"/proc/self/fd/0", "/dev/stdin"},
		{"/proc/self/fd/1", "/dev/stdout"},
		{"/proc/self/fd/2", "/dev/stderr"},
	}
	// kcore support can be toggled with CONFIG_PROC_KCORE; only create a symlink
	// in /dev if it exists in /proc.
	if _, err := os.Stat("/proc/kcore"); err == nil {
		links = append(links, [2]string{"/proc/kcore", "/dev/kcore"})
	}
	for _, link := range links {
		var (
			src = link[0]
			dst = filepath.Join(rootfs, link[1])
		)
		if err := os.Symlink(src, dst); err != nil && !os.IsExist(err) {
			return fmt.Errorf("symlink %s %s %s", src, dst, err)
		}
	}
	return nil
}

// If stdin, stdout or stderr are pointing to '/dev/null' in the global mount namespace,
// this method will make them point to '/dev/null' in this namespace.
func reOpenDevNull(rootfs string) error {
	var stat, devNullStat syscall.Stat_t
	file, err := os.Open(filepath.Join(rootfs, "/dev/null"))
	if err != nil {
		return fmt.Errorf("Failed to open /dev/null - %s", err)
	}
	defer file.Close()
	if err := syscall.Fstat(int(file.Fd()), &devNullStat); err != nil {
		return err
	}
	for fd := 0; fd < 3; fd++ {
		if err := syscall.Fstat(fd, &stat); err != nil {
			return err
		}
		if stat.Rdev == devNullStat.Rdev {
			// Close and re-open the fd.
			if err := syscall.Dup2(int(file.Fd()), fd); err != nil {
				return err
			}
		}
	}
	return nil
}

// Create the device nodes in the container.
func createDevices(config *configs.Config) error {
	oldMask := syscall.Umask(0000)
	for _, node := range config.Devices {
		if err := createDeviceNode(config.Rootfs, node); err != nil {
			syscall.Umask(oldMask)
			return err
		}
	}
	syscall.Umask(oldMask)
	return nil
}

// Creates the device node in the rootfs of the container.
func createDeviceNode(rootfs string, node *configs.Device) error {
	dest := filepath.Join(rootfs, node.Path)
	if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
		return err
	}
	if err := mknodDevice(dest, node); err != nil {
		if os.IsExist(err) {
			return nil
		}
		if err != syscall.EPERM {
			return err
		}
		// containers running in a user namespace are not allowed to mknod
		// devices so we can just bind mount it from the host.
		f, err := os.Create(dest)
		if err != nil && !os.IsExist(err) {
			return err
		}
		if f != nil {
			f.Close()
		}
		return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "")
	}
	return nil
}

func mknodDevice(dest string, node *configs.Device) error {
	fileMode := node.FileMode
	switch node.Type {
	case 'c':
		fileMode |= syscall.S_IFCHR
	case 'b':
		fileMode |= syscall.S_IFBLK
	default:
		return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path)
	}
	if err := syscall.Mknod(dest, uint32(fileMode), node.Mkdev()); err != nil {
		return err
	}
	return syscall.Chown(dest, int(node.Uid), int(node.Gid))
}

func prepareRoot(config *configs.Config) error {
	flag := syscall.MS_PRIVATE | syscall.MS_REC
	if config.NoPivotRoot {
		flag = syscall.MS_SLAVE | syscall.MS_REC
	}
	if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil {
		return err
	}
	return syscall.Mount(config.Rootfs, config.Rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, "")
}

func setReadonly() error {
	return syscall.Mount("/", "/", "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, "")
}

func setupPtmx(config *configs.Config, console *linuxConsole) error {
	ptmx := filepath.Join(config.Rootfs, "dev/ptmx")
	if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) {
		return err
	}
	if err := os.Symlink("pts/ptmx", ptmx); err != nil {
		return fmt.Errorf("symlink dev ptmx %s", err)
	}
	if console != nil {
		return console.mount(config.Rootfs, config.MountLabel, 0, 0)
	}
	return nil
}

func pivotRoot(rootfs, pivotBaseDir string) error {
	if pivotBaseDir == "" {
		pivotBaseDir = "/"
	}
	tmpDir := filepath.Join(rootfs, pivotBaseDir)
	if err := os.MkdirAll(tmpDir, 0755); err != nil {
		return fmt.Errorf("can't create tmp dir %s, error %v", tmpDir, err)
	}
	pivotDir, err := ioutil.TempDir(tmpDir, ".pivot_root")
	if err != nil {
		return fmt.Errorf("can't create pivot_root dir %s, error %v", pivotDir, err)
	}
	if err := syscall.PivotRoot(rootfs, pivotDir); err != nil {
		return fmt.Errorf("pivot_root %s", err)
	}
	if err := syscall.Chdir("/"); err != nil {
		return fmt.Errorf("chdir / %s", err)
	}
	// path to pivot dir now changed, update
	pivotDir = filepath.Join(pivotBaseDir, filepath.Base(pivotDir))
	if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil {
		return fmt.Errorf("unmount pivot_root dir %s", err)
	}
	return os.Remove(pivotDir)
}

func msMoveRoot(rootfs string) error {
	if err := syscall.Mount(rootfs, "/", "", syscall.MS_MOVE, ""); err != nil {
		return err
	}
	if err := syscall.Chroot("."); err != nil {
		return err
	}
	return syscall.Chdir("/")
}

// createIfNotExists creates a file or a directory only if it does not already exist.
func createIfNotExists(path string, isDir bool) error {
	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			if isDir {
				return os.MkdirAll(path, 0755)
			}
			if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
				return err
			}
			f, err := os.OpenFile(path, os.O_CREATE, 0755)
			if err != nil {
				return err
			}
			f.Close()
		}
	}
	return nil
}

// remountReadonly will bind over the top of an existing path and ensure that it is read-only.
func remountReadonly(path string) error {
	for i := 0; i < 5; i++ {
		if err := syscall.Mount("", path, "", syscall.MS_REMOUNT|syscall.MS_RDONLY, ""); err != nil && !os.IsNotExist(err) {
			switch err {
			case syscall.EINVAL:
				// Probably not a mountpoint, use bind-mount
				if err := syscall.Mount(path, path, "", syscall.MS_BIND, ""); err != nil {
					return err
				}
				return syscall.Mount(path, path, "", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC|defaultMountFlags, "")
			case syscall.EBUSY:
				time.Sleep(100 * time.Millisecond)
				continue
			default:
				return err
			}
		}
		return nil
	}
	return fmt.Errorf("unable to mount %s as readonly max retries reached", path)
}

// maskFile bind mounts /dev/null over the top of the specified path inside a container
// to avoid security issues from processes reading information from non-namespace aware mounts ( proc/kcore ).
func maskFile(path string) error {
	if err := syscall.Mount("/dev/null", path, "", syscall.MS_BIND, ""); err != nil && !os.IsNotExist(err) {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"container/list"
	"fmt"
	"github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
	"gopkg.in/sorcix/irc.v2"
	"strings"
	"unicode/utf8"
)

var buffer *list.List
var limit int

func formatmessage(msg *irc.Message) string {
	prefix := msg.Prefix
	if prefix == nil {
		prefix = &irc.Prefix{"unknown", "unknown", "unknown"}
	}
	switch msg.Command {
	case "JOIN":
		return fmt.Sprintf("%s has joined %s\n", prefix.Name, msg.Params[0])
	case "PRIVMSG":
		return fmt.Sprintf("(%s) %s: %s\n", msg.Params[0], prefix.Name, msg.Params[1])
	case "MODE":
		return fmt.Sprintf("%s sets mode %s\n", prefix.Name, strings.Join(msg.Params[0:], " "))
	case "NOTICE":
		return fmt.Sprintf("Notice from %s to %s: %s\n", prefix.Name, msg.Params[0], msg.Params[1])
	case "001":
		fallthrough
	case "002":
		fallthrough
	case "003":
		fallthrough
	case "372":
		fallthrough
	case "375":
		fallthrough
	case "376":
		return fmt.Sprintln(msg.Params[1])
	case "QUIT":
		return fmt.Sprintf("%s has quit (%s)\n", prefix.Name, msg.Params[0])
	case "CTCP":
		if strings.HasPrefix(msg.Params[1], "ACTION") {
			return fmt.Sprintf("%s: * %s %s\n", msg.Params[0], prefix.Name, msg.Params[1][7:])
		} else {
			return fmt.Sprintf("CTCP request from %s to %s: %s\n", prefix.Name, msg.Params[0], msg.Params[1])
		}
	case "CTCPREPLY":
		return fmt.Sprintf("CTCP reply from %s to %s: %s\n", prefix.Name, msg.Params[0], msg.Params[1])
	default:
		return fmt.Sprint("RAW:", msg.String())
	}
}

/* This function should really be in termbox... */
func drawString(x, y int, str string) {
	i := 0
	for _, runeValue := range str {
		putCh(x+i, y, runeValue)
		i += runewidth.RuneWidth(runeValue)
	}
}

func printstring(str string, anchor, width int) int {
	retval := anchor - (len(str) / width)
	retval--
	y := retval
	clearLine(y, width)
	i := 0
	for _, runeValue := range str {
		if i%width == 0 && i >= width {
			y++
			clearLine(y, width)
		}
		putCh(i%width, y, runeValue)
		i += runewidth.RuneWidth(runeValue)
	}
	return retval
}

func updatescreen() {
	width, height := termbox.Size()
	anchor := height - 2
	i := buffer.Front()
	for anchor > 0 && i != nil {
		anchor = printstring(i.Value.(string), anchor, width)
		i = i.Next()
	}
	termbox.Flush()
}

func clearLine(y, width int) {
	for i := 0; i < width; i++ {
		eraseCh(i, y)
	}
}

func putCh(x, y int, ch rune) {
	termbox.SetCell(x, y, ch, termbox.ColorDefault, termbox.ColorDefault)
}

func eraseCh(x, y int) {
	putCh(x, y, ' ')
}

func GetString() string {
	width, height := termbox.Size()
	clearLine(height-1, width)
	retval := ""
	cursor := 0
	tlen := len(target) + 3
	drawString(0, height-1, fmt.Sprint(target, " >"))
	for {
		drawString(tlen, height-1, retval)
		termbox.SetCursor(tlen+cursor, height-1)
		termbox.Flush()
		ev := termbox.PollEvent()
		if ev.Ch == 0 {
			switch ev.Key {
			case termbox.KeySpace:
				retval += " "
				cursor++
			case termbox.KeyEnter:
				if len(retval) > 0 {
					termbox.HideCursor()
					return retval
				}
			case termbox.KeyDelete:
				fallthrough
			case termbox.KeyBackspace:
				if cursor > 0 {
					r, rs :=
						utf8.DecodeLastRuneInString(retval)
					retval = retval[0 : len(retval)-rs]
					eraseCh(cursor+(tlen)-runewidth.RuneWidth(r), height-1)
					cursor -= runewidth.RuneWidth(r)
				}
			}
		} else if ev.Ch > 31 {
			retval += string(ev.Ch)
			cursor += runewidth.RuneWidth(ev.Ch)
		}
	}
}

func sendtobuffer(str string) {
	buffer.PushFront(StripMircFormatting(str))
	if buffer.Len() > limit {
		buffer.Remove(buffer.Back())
	}
}

func printmsg(msg *irc.Message) {
	sendtobuffer(formatmessage(msg))
}

func initscreen() {
	termbox.Init()
}

func outputloop(client *Client, scrollback int) {
	limit = scrollback
	buffer = list.New()
	for {
		msg, err := client.Receive()
		if err != nil {
			fmt.Println("Output loop closing:", err)
			return
		}
		if msg != nil {
			if msg.Command != "PING" {
				printmsg(msg)
			}
			if msg.Command == "ERROR" {
				return
			}
		}
		updatescreen()
	}
}

func inputloop(client *Client) {
	defer termbox.Close()
	for {
		text := GetString()
		sendtobuffer(fmt.Sprint("(", target, ") ", text))
		updatescreen()
		if Command(client, text) {
			return
		}
	}
}
<commit_msg>change /me format for consistency<commit_after>package main

import (
	"container/list"
	"fmt"
	"github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
	"gopkg.in/sorcix/irc.v2"
	"strings"
	"unicode/utf8"
)

var buffer *list.List
var limit int

func formatmessage(msg *irc.Message) string {
	prefix := msg.Prefix
	if prefix == nil {
		prefix = &irc.Prefix{"unknown", "unknown", "unknown"}
	}
	switch msg.Command {
	case "JOIN":
		return fmt.Sprintf("%s has joined %s\n", prefix.Name, msg.Params[0])
	case "PRIVMSG":
		return fmt.Sprintf("(%s) %s: %s\n", msg.Params[0], prefix.Name, msg.Params[1])
	case "MODE":
		return fmt.Sprintf("%s sets mode %s\n", prefix.Name, strings.Join(msg.Params[0:], " "))
	case "NOTICE":
		return fmt.Sprintf("Notice from %s to %s: %s\n", prefix.Name, msg.Params[0], msg.Params[1])
	case "001":
		fallthrough
	case "002":
		fallthrough
	case "003":
		fallthrough
	case "372":
		fallthrough
	case "375":
		fallthrough
	case "376":
		return fmt.Sprintln(msg.Params[1])
	case "QUIT":
		return fmt.Sprintf("%s has quit (%s)\n", prefix.Name, msg.Params[0])
	case "CTCP":
		if strings.HasPrefix(msg.Params[1], "ACTION") {
			return fmt.Sprintf("(%s) * %s %s\n", msg.Params[0], prefix.Name, msg.Params[1][7:])
		} else {
			return fmt.Sprintf("CTCP request from %s to %s: %s\n", prefix.Name, msg.Params[0], msg.Params[1])
		}
	case "CTCPREPLY":
		return fmt.Sprintf("CTCP reply from %s to %s: %s\n", prefix.Name, msg.Params[0], msg.Params[1])
	default:
		return fmt.Sprint("RAW:", msg.String())
	}
}

/* This function should really be in termbox... */
func drawString(x, y int, str string) {
	i := 0
	for _, runeValue := range str {
		putCh(x+i, y, runeValue)
		i += runewidth.RuneWidth(runeValue)
	}
}

func printstring(str string, anchor, width int) int {
	retval := anchor - (len(str) / width)
	retval--
	y := retval
	clearLine(y, width)
	i := 0
	for _, runeValue := range str {
		if i%width == 0 && i >= width {
			y++
			clearLine(y, width)
		}
		putCh(i%width, y, runeValue)
		i += runewidth.RuneWidth(runeValue)
	}
	return retval
}

func updatescreen() {
	width, height := termbox.Size()
	anchor := height - 2
	i := buffer.Front()
	for anchor > 0 && i != nil {
		anchor = printstring(i.Value.(string), anchor, width)
		i = i.Next()
	}
	termbox.Flush()
}

func clearLine(y, width int) {
	for i := 0; i < width; i++ {
		eraseCh(i, y)
	}
}

func putCh(x, y int, ch rune) {
	termbox.SetCell(x, y, ch, termbox.ColorDefault, termbox.ColorDefault)
}

func eraseCh(x, y int) {
	putCh(x, y, ' ')
}

func GetString() string {
	width, height := termbox.Size()
	clearLine(height-1, width)
	retval := ""
	cursor := 0
	tlen := len(target) + 3
	drawString(0, height-1, fmt.Sprint(target, " >"))
	for {
		drawString(tlen, height-1, retval)
		termbox.SetCursor(tlen+cursor, height-1)
		termbox.Flush()
		ev := termbox.PollEvent()
		if ev.Ch == 0 {
			switch ev.Key {
			case termbox.KeySpace:
				retval += " "
				cursor++
			case termbox.KeyEnter:
				if len(retval) > 0 {
					termbox.HideCursor()
					return retval
				}
			case termbox.KeyDelete:
				fallthrough
			case termbox.KeyBackspace:
				if cursor > 0 {
					r, rs :=
						utf8.DecodeLastRuneInString(retval)
					retval = retval[0 : len(retval)-rs]
					eraseCh(cursor+(tlen)-runewidth.RuneWidth(r), height-1)
					cursor -= runewidth.RuneWidth(r)
				}
			}
		} else if ev.Ch > 31 {
			retval += string(ev.Ch)
			cursor += runewidth.RuneWidth(ev.Ch)
		}
	}
}

func sendtobuffer(str string) {
	buffer.PushFront(StripMircFormatting(str))
	if buffer.Len() > limit {
		buffer.Remove(buffer.Back())
	}
}

func printmsg(msg *irc.Message) {
	sendtobuffer(formatmessage(msg))
}

func initscreen() {
	termbox.Init()
}

func outputloop(client *Client, scrollback int) {
	limit = scrollback
	buffer = list.New()
	for {
		msg, err := client.Receive()
		if err != nil {
			fmt.Println("Output loop closing:", err)
			return
		}
		if msg != nil {
			if msg.Command != "PING" {
				printmsg(msg)
			}
			if msg.Command == "ERROR" {
				return
			}
		}
		updatescreen()
	}
}

func inputloop(client *Client) {
	defer termbox.Close()
	for {
		text := GetString()
		sendtobuffer(fmt.Sprint("(", target, ") ", text))
		updatescreen()
		if Command(client, text) {
			return
		}
	}
}
<|endoftext|>


<commit_before>package base58

import (
	"encoding/hex"
	"fmt"
	"testing"
)

var TestTable = [][]string{
	{"61", "2g", "C2dGTwc"},
	{"626262", "a3gV", "4jF5uERJAK"},
	{"636363", "aPEr", "4mT4krqUYJ"},
	{"73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2", "BXF1HuEUCqeVzZdrKeJjG74rjeXxqJ7dW"},
	{"00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L", "13REmUhe2ckUKy1FvM7AMCdtyYq831yxM3QeyEu4"},
	{"516b6fcd0f", "ABnLTmg", "237LSrY9NUUas"},
	{"bf4f89001e670274dd", "3SEo3LWLoPntC", "GwDDDeduj1jpykc27e"},
	{"572e4794", "3EFU7m", "FamExfqCeza"},
	{"ecac89cad93923c02321", "EJDM8drfXA6uyA", "2W1Yd5Zu6WGyKVtHGMrH"},
	{"10c8511e", "Rt5zm", "3op3iuGMmhs"},
	{"00000000000000000000", "1111111111", "111111111146Momb"},
	{"", "", "3QJmnh"},
}

func TestEncode(t *testing.T) {

	for _, line := range TestTable {
		test := []byte(line[0])

		b10 := make([]byte, hex.DecodedLen(len(test)))
		hex.Decode(b10, test)
		// This method accepts base10
		hash, _ := Encode(b10)

		if string(hash) != line[1] {
			t.Error(line[0])
			t.Errorf("Encode mismatch expected: %s was: %s",
				line[1], hash)
		}

		if b := Check(b10); b != line[2] {
			t.Errorf("Encode check failed expected: %s was: %s",
				line[2], b)
		}
	}
}

func ExampleEncode_hex() {
	// Decode hex prior to passing it to Encode
	h := []byte("bf4f89001e670274dd")
	ten := make([]byte, hex.DecodedLen(len(h)))
	hex.Decode(ten, h)
	fmt.Printf("base10: %d\n", ten)
	fmt.Printf("hex: %x\n", ten)

	fifty, len := Encode(ten)
	fmt.Printf("len: %d text: %s\n", len, fifty)
	// Output:
	// base10: [191 79 137 0 30 103 2 116 221]
	// hex: bf4f89001e670274dd
	// len: 13 text: 3SEo3LWLoPntC
}

func ExampleDecode() {
	fmt.Println("Hello")
}

func TestDecode(t *testing.T) {
	for _, line := range TestTable {
		hash, _, err := Decode([]byte(line[1]))
		if err != nil {
			t.Error(err)
		}
		b10 := make([]byte, hex.EncodedLen(len(hash)))
		hex.Encode(b10, hash)
		if string(b10) != line[0] {
			t.Error(line[0])
			t.Errorf("Decode mismatch expected: %s was: %s",
				line[1], b10)
		}

	}
}
<commit_msg>Decoding a bitcoin address example<commit_after>package base58

import (
	"encoding/hex"
	"fmt"
	"testing"
)

var TestTable = [][]string{
	{"61", "2g", "C2dGTwc"},
	{"626262", "a3gV", "4jF5uERJAK"},
	{"636363", "aPEr", "4mT4krqUYJ"},
	{"73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2", "BXF1HuEUCqeVzZdrKeJjG74rjeXxqJ7dW"},
	{"00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L", "13REmUhe2ckUKy1FvM7AMCdtyYq831yxM3QeyEu4"},
	{"516b6fcd0f", "ABnLTmg", "237LSrY9NUUas"},
	{"bf4f89001e670274dd", "3SEo3LWLoPntC", "GwDDDeduj1jpykc27e"},
	{"572e4794", "3EFU7m", "FamExfqCeza"},
	{"ecac89cad93923c02321", "EJDM8drfXA6uyA", "2W1Yd5Zu6WGyKVtHGMrH"},
	{"10c8511e", "Rt5zm", "3op3iuGMmhs"},
	{"00000000000000000000", "1111111111", "111111111146Momb"},
	{"", "", "3QJmnh"},
}

func TestEncode(t *testing.T) {

	for _, line := range TestTable {
		test := []byte(line[0])

		b10 := make([]byte, hex.DecodedLen(len(test)))
		hex.Decode(b10, test)
		// This method accepts base10
		hash, _ := Encode(b10)

		if string(hash) != line[1] {
			t.Error(line[0])
			t.Errorf("Encode mismatch expected: %s was: %s",
				line[1], hash)
		}

		if b := Check(b10); b != line[2] {
			t.Errorf("Encode check failed expected: %s was: %s",
				line[2], b)
		}
	}
}

func ExampleEncode_hex() {
	// Decode hex prior to passing it to Encode
	h := []byte("bf4f89001e670274dd")
	ten := make([]byte, hex.DecodedLen(len(h)))
	hex.Decode(ten, h)
	fmt.Printf("base10: %d\n", ten)
	fmt.Printf("hex: %x\n", ten)

	fifty, len := Encode(ten)
	fmt.Printf("len: %d text: %s\n", len, fifty)
	// Output:
	// base10: [191 79 137 0 30 103 2 116 221]
	// hex: bf4f89001e670274dd
	// len: 13 text: 3SEo3LWLoPntC
}

func ExampleDecode_bitaddress() {
	addr := []byte("1PbyRQXKNCwcmn43ZpJ4pxFEAvnG8iAc6J")
	fmt.Printf("Bitcoin addresses are %d characters long\n", len(addr))
	decoded, len, err := Decode(addr)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Printf("Length of byte slice: %d\n", len)
	fmt.Printf("hex encoded base10: %x\n", decoded)
	fmt.Printf("Byte slice: %v\n", decoded)
	// Output:
	// Bitcoin addresses are 34 characters long
	// Length of byte slice: 25
	// hex encoded base10: 00f7f001f4f3c2e3ea60440f4e3f97d9dd5266345d234d30b7
	// Byte slice: [0 247 240 1 244 243 194 227 234 96 68 15 78 63 151 217 221 82 102 52 93 35 77 48 183]
}

func TestDecode(t *testing.T) {
	for _, line := range TestTable {
		hash, _, err := Decode([]byte(line[1]))
		if err != nil {
			t.Error(err)
		}
		b10 := make([]byte, hex.EncodedLen(len(hash)))
		hex.Encode(b10, hash)
		if string(b10) != line[0] {
			t.Error(line[0])
			t.Errorf("Decode mismatch expected: %s was: %s",
				line[1], b10)
		}

	}
}
<|endoftext|>


<commit_before>package features

import (
	"bytes"
	"errors"
	"fmt"
	"sync"

	"github.com/cilium/ebpf"
	"github.com/cilium/ebpf/asm"
	"github.com/cilium/ebpf/internal"
	"github.com/cilium/ebpf/internal/sys"
	"github.com/cilium/ebpf/internal/unix"
)

func init() {
	miscs.miscTypes = make(map[miscType]error)
}

var (
	miscs miscCache
)

type miscCache struct {
	sync.Mutex
	miscTypes map[miscType]error
}

type miscType uint32

const (
	// largeInsn support introduced in Linux 5.2
	// commit c04c0d2b968ac45d6ef020316808ef6c82325a82
	largeInsn miscType = iota
	// boundedLoops support introduced in Linux 5.3
	// commit 2589726d12a1b12eaaa93c7f1ea64287e383c7a5
	boundedLoops
	// v2ISA support introduced in Linux 4.14
	// commit 92b31a9af73b3a3fc801899335d6c47966351830
	v2ISA
	// v3ISA support introduced in Linux 5.1
	// commit 092ed0968bb648cd18e8a0430cd0a8a71727315c
	v3ISA
)

const (
	maxInsns = 4096
)

// HaveLargeInstructions probes the running kernel if more than 4096 instructions
// per program are supported.
//
// See the package documentation for the meaning of the error return value.
func HaveLargeInstructions() error {
	return probeMisc(largeInsn)
}

// HaveBoundedLoops probes the running kernel if bounded loops are supported.
//
// See the package documentation for the meaning of the error return value.
func HaveBoundedLoops() error {
	return probeMisc(boundedLoops)
}

// HaveV2ISA probes the running kernel if instructions of the v2 ISA are supported.
//
// See the package documentation for the meaning of the error return value.
func HaveV2ISA() error {
	return probeMisc(v2ISA)
}

// HaveV3ISA probes the running kernel if instructions of the v3 ISA are supported.
//
// See the package documentation for the meaning of the error return value.
func HaveV3ISA() error {
	return probeMisc(v3ISA)
}

// probeMisc checks the kernel for a given supported misc by creating
// a specialized program probe and loading it.
func probeMisc(mt miscType) (err error) {
	defer func() {
		// This closure modifies a named return variable.
		err = wrapProbeErrors(err)
	}()

	mc.Lock()
	defer mc.Unlock()
	err, ok := miscs.miscTypes[mt]
	if ok {
		return err
	}

	attr, err := createMiscProbeAttr(mt)
	if err != nil {
		return fmt.Errorf("couldn't create the attributes for the probe: %w", err)
	}

	fd, err := sys.ProgLoad(attr)
	if err == nil {
		fd.Close()
	}

	switch {
	// EINVAL occurs when attempting to create a program with an unknown type.
	// E2BIG occurs when ProgLoadAttr contains non-zero bytes past the end
	// of the struct known by the running kernel, meaning the kernel is too old
	// to support the given map type.
	case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):
		err = ebpf.ErrNotSupported
	}

	miscs.miscTypes[mt] = err

	return err
}

func createMiscProbeAttr(mt miscType) (*sys.ProgLoadAttr, error) {
	var insns asm.Instructions
	switch mt {
	case largeInsn:
		for i := 0; i < maxInsns; i++ {
			insns = append(insns, asm.Mov.Imm(asm.R0, 1))
		}
		insns = append(insns, asm.Return())
	case boundedLoops:
		insns = asm.Instructions{
			asm.Mov.Imm(asm.R0, 10),
			asm.Sub.Imm(asm.R0, 1).WithSymbol("loop"),
			asm.JNE.Imm(asm.R0, 0, "loop"),
			asm.Return(),
		}
	case v2ISA:
		insns = asm.Instructions{
			asm.Mov.Imm(asm.R0, 0),
			asm.JLT.Imm(asm.R0, 0, "exit"),
			asm.Mov.Imm(asm.R0, 1),
			asm.Return().WithSymbol("exit"),
		}
	case v3ISA:
		insns = asm.Instructions{
			asm.Mov.Imm(asm.R0, 0),
			asm.JLT.Imm32(asm.R0, 0, "exit"),
			asm.Mov.Imm(asm.R0, 1),
			asm.Return().WithSymbol("exit"),
		}
	default:
		return nil, fmt.Errorf("misc probe %d not implemented", mt)
	}

	buf := bytes.NewBuffer(make([]byte, 0, insns.Size()))
	if err := insns.Marshal(buf, internal.NativeEndian); err != nil {
		return nil, err
	}

	bytecode := buf.Bytes()
	instructions := sys.NewSlicePointer(bytecode)

	return &sys.ProgLoadAttr{
		ProgType: sys.BPF_PROG_TYPE_SOCKET_FILTER,
		Insns:    instructions,
		InsnCnt:  uint32(len(bytecode) / asm.InstructionSize),
		License:  sys.NewStringPointer("MIT"),
	}, nil
}
<commit_msg>features: fix incorrect lock in probeMisc<commit_after>package features

import (
	"bytes"
	"errors"
	"fmt"
	"sync"

	"github.com/cilium/ebpf"
	"github.com/cilium/ebpf/asm"
	"github.com/cilium/ebpf/internal"
	"github.com/cilium/ebpf/internal/sys"
	"github.com/cilium/ebpf/internal/unix"
)

func init() {
	miscs.miscTypes = make(map[miscType]error)
}

var (
	miscs miscCache
)

type miscCache struct {
	sync.Mutex
	miscTypes map[miscType]error
}

type miscType uint32

const (
	// largeInsn support introduced in Linux 5.2
	// commit c04c0d2b968ac45d6ef020316808ef6c82325a82
	largeInsn miscType = iota
	// boundedLoops support introduced in Linux 5.3
	// commit 2589726d12a1b12eaaa93c7f1ea64287e383c7a5
	boundedLoops
	// v2ISA support introduced in Linux 4.14
	// commit 92b31a9af73b3a3fc801899335d6c47966351830
	v2ISA
	// v3ISA support introduced in Linux 5.1
	// commit 092ed0968bb648cd18e8a0430cd0a8a71727315c
	v3ISA
)

const (
	maxInsns = 4096
)

// HaveLargeInstructions probes the running kernel if more than 4096 instructions
// per program are supported.
//
// See the package documentation for the meaning of the error return value.
func HaveLargeInstructions() error {
	return probeMisc(largeInsn)
}

// HaveBoundedLoops probes the running kernel if bounded loops are supported.
//
// See the package documentation for the meaning of the error return value.
func HaveBoundedLoops() error {
	return probeMisc(boundedLoops)
}

// HaveV2ISA probes the running kernel if instructions of the v2 ISA are supported.
//
// See the package documentation for the meaning of the error return value.
func HaveV2ISA() error {
	return probeMisc(v2ISA)
}

// HaveV3ISA probes the running kernel if instructions of the v3 ISA are supported.
//
// See the package documentation for the meaning of the error return value.
func HaveV3ISA() error {
	return probeMisc(v3ISA)
}

// probeMisc checks the kernel for a given supported misc by creating
// a specialized program probe and loading it.
func probeMisc(mt miscType) (err error) {
	defer func() {
		// This closure modifies a named return variable.
		err = wrapProbeErrors(err)
	}()

	miscs.Lock()
	defer miscs.Unlock()
	err, ok := miscs.miscTypes[mt]
	if ok {
		return err
	}

	attr, err := createMiscProbeAttr(mt)
	if err != nil {
		return fmt.Errorf("couldn't create the attributes for the probe: %w", err)
	}

	fd, err := sys.ProgLoad(attr)
	if err == nil {
		fd.Close()
	}

	switch {
	// EINVAL occurs when attempting to create a program with an unknown type.
	// E2BIG occurs when ProgLoadAttr contains non-zero bytes past the end
	// of the struct known by the running kernel, meaning the kernel is too old
	// to support the given map type.
	case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):
		err = ebpf.ErrNotSupported
	}

	miscs.miscTypes[mt] = err

	return err
}

func createMiscProbeAttr(mt miscType) (*sys.ProgLoadAttr, error) {
	var insns asm.Instructions
	switch mt {
	case largeInsn:
		for i := 0; i < maxInsns; i++ {
			insns = append(insns, asm.Mov.Imm(asm.R0, 1))
		}
		insns = append(insns, asm.Return())
	case boundedLoops:
		insns = asm.Instructions{
			asm.Mov.Imm(asm.R0, 10),
			asm.Sub.Imm(asm.R0, 1).WithSymbol("loop"),
			asm.JNE.Imm(asm.R0, 0, "loop"),
			asm.Return(),
		}
	case v2ISA:
		insns = asm.Instructions{
			asm.Mov.Imm(asm.R0, 0),
			asm.JLT.Imm(asm.R0, 0, "exit"),
			asm.Mov.Imm(asm.R0, 1),
			asm.Return().WithSymbol("exit"),
		}
	case v3ISA:
		insns = asm.Instructions{
			asm.Mov.Imm(asm.R0, 0),
			asm.JLT.Imm32(asm.R0, 0, "exit"),
			asm.Mov.Imm(asm.R0, 1),
			asm.Return().WithSymbol("exit"),
		}
	default:
		return nil, fmt.Errorf("misc probe %d not implemented", mt)
	}

	buf := bytes.NewBuffer(make([]byte, 0, insns.Size()))
	if err := insns.Marshal(buf, internal.NativeEndian); err != nil {
		return nil, err
	}

	bytecode := buf.Bytes()
	instructions := sys.NewSlicePointer(bytecode)

	return &sys.ProgLoadAttr{
		ProgType: sys.BPF_PROG_TYPE_SOCKET_FILTER,
		Insns:    instructions,
		InsnCnt:  uint32(len(bytecode) / asm.InstructionSize),
		License:  sys.NewStringPointer("MIT"),
	}, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package magnum

import (
	"fmt"
	"sync"
	"time"

	apiv1 "k8s.io/api/core/v1"
	"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
	klog "k8s.io/klog/v2"
	schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
)

// magnumNodeGroup implements NodeGroup interface from cluster-autoscaler/cloudprovider.
//
// Represents a homogeneous collection of nodes within a cluster,
// which can be dynamically resized between a minimum and maximum
// number of nodes.
type magnumNodeGroup struct {
	magnumManager magnumManager
	id            string

	clusterUpdateMutex *sync.Mutex

	minSize int
	maxSize int
	// Stored as a pointer so that when autoscaler copies the nodegroup it can still update the target size
	targetSize *int

	nodesToDelete      []*apiv1.Node
	nodesToDeleteMutex sync.Mutex

	waitTimeStep        time.Duration
	deleteBatchingDelay time.Duration

	// Used so that only one DeleteNodes goroutine has to get the node group size at the start of the deletion
	deleteNodesCachedSize   int
	deleteNodesCachedSizeAt time.Time
}

// waitForClusterStatus checks periodically to see if the cluster has entered a given status.
// Returns when the status is observed or the timeout is reached.
func (ng *magnumNodeGroup) waitForClusterStatus(status string, timeout time.Duration) error {
	klog.V(2).Infof("Waiting for cluster %s status", status)
	for start := time.Now(); time.Since(start) < timeout; time.Sleep(ng.waitTimeStep) {
		clusterStatus, err := ng.magnumManager.getClusterStatus()
		if err != nil {
			return fmt.Errorf("error waiting for %s status: %v", status, err)
		}
		if clusterStatus == status {
			klog.V(0).Infof("Waited for cluster %s status", status)
			return nil
		}
	}
	return fmt.Errorf("timeout (%v) waiting for %s status", timeout, status)
}

// IncreaseSize increases the number of nodes by replacing the cluster's node_count.
//
// Takes precautions so that the cluster is not modified while in an UPDATE_IN_PROGRESS state.
// Blocks until the cluster has reached UPDATE_COMPLETE.
func (ng *magnumNodeGroup) IncreaseSize(delta int) error {
	ng.clusterUpdateMutex.Lock()
	defer ng.clusterUpdateMutex.Unlock()

	if delta <= 0 {
		return fmt.Errorf("size increase must be positive")
	}

	size, err := ng.magnumManager.nodeGroupSize(ng.id)
	if err != nil {
		return fmt.Errorf("could not check current nodegroup size: %v", err)
	}
	if size+delta > ng.MaxSize() {
		return fmt.Errorf("size increase too large, desired:%d max:%d", size+delta, ng.MaxSize())
	}

	updatePossible, currentStatus, err := ng.magnumManager.canUpdate()
	if err != nil {
		return fmt.Errorf("can not increase node count: %v", err)
	}
	if !updatePossible {
		return fmt.Errorf("can not add nodes, cluster is in %s status", currentStatus)
	}
	klog.V(0).Infof("Increasing size by %d, %d->%d", delta, *ng.targetSize, *ng.targetSize+delta)
	*ng.targetSize += delta

	err = ng.magnumManager.updateNodeCount(ng.id, *ng.targetSize)
	if err != nil {
		return fmt.Errorf("could not increase cluster size: %v", err)
	}

	// Block until cluster has gone into update status and then completed
	err = ng.waitForClusterStatus(clusterStatusUpdateInProgress, waitForUpdateStatusTimeout)
	if err != nil {
		return fmt.Errorf("wait for cluster status failed: %v", err)
	}
	err = ng.waitForClusterStatus(clusterStatusUpdateComplete, waitForCompleteStatusTimout)
	if err != nil {
		return fmt.Errorf("wait for cluster status failed: %v", err)
	}
	return nil
}

// deleteNodes deletes a set of nodes chosen by the autoscaler.
//
// The process of deletion depends on the implementation of magnumManager,
// but this function handles what should be common between all implementations:
//   - simultaneous but separate calls from the autoscaler are batched together
//   - does not allow scaling while the cluster is already in an UPDATE_IN_PROGRESS state
//   - after scaling down, blocks until the cluster has reached UPDATE_COMPLETE
func (ng *magnumNodeGroup) DeleteNodes(nodes []*apiv1.Node) error {

	// Batch simultaneous deletes on individual nodes
	ng.nodesToDeleteMutex.Lock()

	// First get the node group size and store the value, so that any other parallel delete calls can use it
	// without having to make the get request for the cluster themselves.
	// cachedSize keeps a local copy for this goroutine, so that ng.deleteNodesCachedSize is used
	// only within the ng.nodesToDeleteMutex.
	var cachedSize int
	var err error
	if time.Since(ng.deleteNodesCachedSizeAt) > time.Second*10 {
		cachedSize, err = ng.magnumManager.nodeGroupSize(ng.id)
		if err != nil {
			ng.nodesToDeleteMutex.Unlock()
			return fmt.Errorf("could not get current node count: %v", err)
		}
		ng.deleteNodesCachedSize = cachedSize
		ng.deleteNodesCachedSizeAt = time.Now()
	} else {
		cachedSize = ng.deleteNodesCachedSize
	}

	// Check that these nodes would not make the batch delete more nodes than the minimum would allow
	if cachedSize-len(ng.nodesToDelete)-len(nodes) < ng.MinSize() {
		ng.nodesToDeleteMutex.Unlock()
		return fmt.Errorf("deleting nodes would take nodegroup below minimum size")
	}
	// otherwise, add the nodes to the batch and release the lock
	ng.nodesToDelete = append(ng.nodesToDelete, nodes...)
	ng.nodesToDeleteMutex.Unlock()

	// The first of the parallel delete calls to obtain this lock will be the one to actually perform the deletion
	ng.clusterUpdateMutex.Lock()
	defer ng.clusterUpdateMutex.Unlock()

	ng.nodesToDeleteMutex.Lock()
	if len(ng.nodesToDelete) == 0 {
		// Deletion was handled by another goroutine
		ng.nodesToDeleteMutex.Unlock()
		return nil
	}
	ng.nodesToDeleteMutex.Unlock()

	// This goroutine has the clusterUpdateMutex, so will be the one
	// to actually delete the nodes. While this goroutine waits, others
	// will add their nodes to nodesToDelete and block at acquiring
	// the clusterUpdateMutex lock. One they get it, the deletion will
	// already be done and they will return above at the check
	// for len(ng.nodesToDelete) == 0.
	time.Sleep(ng.deleteBatchingDelay)

	ng.nodesToDeleteMutex.Lock()
	nodes = make([]*apiv1.Node, len(ng.nodesToDelete))
	copy(nodes, ng.nodesToDelete)
	ng.nodesToDelete = nil
	ng.nodesToDeleteMutex.Unlock()

	var nodeNames []string
	for _, node := range nodes {
		nodeNames = append(nodeNames, node.Name)
	}
	klog.V(1).Infof("Deleting nodes: %v", nodeNames)

	updatePossible, currentStatus, err := ng.magnumManager.canUpdate()
	if err != nil {
		return fmt.Errorf("could not check if cluster is ready to delete nodes: %v", err)
	}
	if !updatePossible {
		return fmt.Errorf("can not delete nodes, cluster is in %s status", currentStatus)
	}

	// Double check that the total number of batched nodes for deletion will not take the node group below its minimum size
	if cachedSize-len(nodes) < ng.MinSize() {
		return fmt.Errorf("size decrease too large, desired:%d min:%d", cachedSize-len(nodes), ng.MinSize())
	}

	var nodeRefs []NodeRef
	for _, node := range nodes {

		// Find node IPs, can be multiple (IPv4 and IPv6)
		var IPs []string
		for _, addr := range node.Status.Addresses {
			if addr.Type == apiv1.NodeInternalIP {
				IPs = append(IPs, addr.Address)
			}
		}
		nodeRefs = append(nodeRefs, NodeRef{
			Name:       node.Name,
			MachineID:  node.Status.NodeInfo.MachineID,
			ProviderID: node.Spec.ProviderID,
			IPs:        IPs,
		})
	}

	err = ng.magnumManager.deleteNodes(ng.id, nodeRefs, cachedSize-len(nodes))
	if err != nil {
		return fmt.Errorf("manager error deleting nodes: %v", err)
	}

	// Block until cluster has gone into update status and then completed
	err = ng.waitForClusterStatus(clusterStatusUpdateInProgress, waitForUpdateStatusTimeout)
	if err != nil {
		return fmt.Errorf("wait for cluster status failed: %v", err)
	}
	err = ng.waitForClusterStatus(clusterStatusUpdateComplete, waitForCompleteStatusTimout)
	if err != nil {
		return fmt.Errorf("wait for cluster status failed: %v", err)
	}

	// Check the new node group size and store that as the new target
	newSize, err := ng.magnumManager.nodeGroupSize(ng.id)
	if err != nil {
		// Set to the expected size as a fallback
		*ng.targetSize = cachedSize - len(nodes)
		return fmt.Errorf("could not check new cluster size after scale down: %v", err)
	}
	*ng.targetSize = newSize

	return nil
}

// DecreaseTargetSize decreases the cluster node_count in magnum.
func (ng *magnumNodeGroup) DecreaseTargetSize(delta int) error {
	if delta >= 0 {
		return fmt.Errorf("size decrease must be negative")
	}
	klog.V(0).Infof("Decreasing target size by %d, %d->%d", delta, *ng.targetSize, *ng.targetSize+delta)
	*ng.targetSize += delta
	return ng.magnumManager.updateNodeCount(ng.id, *ng.targetSize)
}

// Id returns the node group ID
func (ng *magnumNodeGroup) Id() string {
	return ng.id
}

// Debug returns a string formatted with the node group's min, max and target sizes.
func (ng *magnumNodeGroup) Debug() string {
	return fmt.Sprintf("%s min=%d max=%d target=%d", ng.id, ng.minSize, ng.maxSize, *ng.targetSize)
}

// Nodes returns a list of nodes that belong to this node group.
func (ng *magnumNodeGroup) Nodes() ([]cloudprovider.Instance, error) {
	nodes, err := ng.magnumManager.getNodes(ng.id)
	if err != nil {
		return nil, fmt.Errorf("could not get nodes: %v", err)
	}
	var instances []cloudprovider.Instance
	for _, node := range nodes {
		instances = append(instances, cloudprovider.Instance{Id: node})
	}
	return instances, nil
}

// TemplateNodeInfo returns a node template for this node group.
func (ng *magnumNodeGroup) TemplateNodeInfo() (*schedulerframework.NodeInfo, error) {
	return ng.magnumManager.templateNodeInfo(ng.id)
}

// Exist returns if this node group exists.
// Currently always returns true.
func (ng *magnumNodeGroup) Exist() bool {
	return true
}

// Create creates the node group on the cloud provider side.
func (ng *magnumNodeGroup) Create() (cloudprovider.NodeGroup, error) {
	return nil, cloudprovider.ErrAlreadyExist
}

// Delete deletes the node group on the cloud provider side.
func (ng *magnumNodeGroup) Delete() error {
	return cloudprovider.ErrNotImplemented
}

// Autoprovisioned returns if the nodegroup is autoprovisioned.
func (ng *magnumNodeGroup) Autoprovisioned() bool {
	return false
}

// MaxSize returns the maximum allowed size of the node group.
func (ng *magnumNodeGroup) MaxSize() int {
	return ng.maxSize
}

// MinSize returns the minimum allowed size of the node group.
func (ng *magnumNodeGroup) MinSize() int {
	return ng.minSize
}

// TargetSize returns the target size of the node group.
func (ng *magnumNodeGroup) TargetSize() (int, error) {
	return *ng.targetSize, nil
}
<commit_msg>Update magnum node group to use new manager<commit_after>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package magnum

import (
	"fmt"
	"sync"
	"time"

	apiv1 "k8s.io/api/core/v1"
	"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
	klog "k8s.io/klog/v2"
	schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
)

// How long to sleep after deleting nodes, to ensure that multiple requests arrive in order.
var postDeleteSleepDuration = 5 * time.Second

// magnumNodeGroup implements NodeGroup interface from cluster-autoscaler/cloudprovider.
//
// Represents a homogeneous collection of nodes within a cluster,
// which can be dynamically resized between a minimum and maximum
// number of nodes.
type magnumNodeGroup struct {
	magnumManager magnumManager

	// Human readable ID for logs and CA status configmap.
	id string
	// Node group UUID in Magnum.
	UUID string

	// To be locked when resizing the cluster, or reading
	// cluster state that could be being modified.
	// Shared between all node groups.
	clusterUpdateLock *sync.Mutex

	minSize    int
	maxSize    int
	targetSize int

	// deletedNodes tracks nodes which have been requested for deletion.
	// Heat can't always delete a node immediately if there is another concurrent update,
	// so reporting a node as being in a failed state multiple times can cause the autoscaler
	// to try to repeatedly delete it.
	// Maps provider ID -> time of deletion request.
	deletedNodes map[string]time.Time
}

// IncreaseSize increases the number of nodes by replacing the cluster's node_count.
//
// Takes precautions so that the cluster is not modified while in an UPDATE_IN_PROGRESS state.
// Blocks until the cluster has reached UPDATE_COMPLETE.
func (ng *magnumNodeGroup) IncreaseSize(delta int) error {
	ng.clusterUpdateLock.Lock()
	defer ng.clusterUpdateLock.Unlock()

	if delta <= 0 {
		return fmt.Errorf("size increase must be positive")
	}

	size := ng.targetSize
	if size+delta > ng.MaxSize() {
		return fmt.Errorf("size increase too large, desired:%d max:%d", size+delta, ng.MaxSize())
	}

	klog.V(2).Infof("Increasing size by %d, %d->%d", delta, size, size+delta)
	err := ng.magnumManager.updateNodeCount(ng.UUID, size+delta)
	if err != nil {
		return fmt.Errorf("could not increase cluster size: %v", err)
	}

	ng.targetSize += delta
	return nil
}

// deleteNodes deletes a set of nodes chosen by the autoscaler.
func (ng *magnumNodeGroup) DeleteNodes(nodes []*apiv1.Node) error {
	ng.clusterUpdateLock.Lock()
	defer ng.clusterUpdateLock.Unlock()

	size := ng.targetSize

	var nodeNames []string
	for _, node := range nodes {
		nodeNames = append(nodeNames, node.Name)
	}
	klog.V(2).Infof("Deleting nodes: %v", nodeNames)

	// Check that the total number of nodes to be deleted will not take the node group below its minimum size
	if size-len(nodes) < ng.MinSize() {
		return fmt.Errorf("size decrease too large, desired:%d min:%d", size-len(nodes), ng.MinSize())
	}

	var nodeRefs []NodeRef
	for _, node := range nodes {
		nodeRefs = append(nodeRefs, NodeRef{
			Name:       node.Name,
			SystemUUID: node.Status.NodeInfo.SystemUUID,
			ProviderID: node.Spec.ProviderID,
			IsFake:     isFakeNode(node),
		})
	}

	err := ng.magnumManager.deleteNodes(ng.UUID, nodeRefs, size-len(nodes))
	if err != nil {
		return fmt.Errorf("manager error deleting nodes: %v", err)
	}

	ng.targetSize = size - len(nodes)

	now := time.Now()
	for _, node := range nodes {
		ng.deletedNodes[node.Spec.ProviderID] = now
	}

	// Sleep for a few seconds to ensure that delete requests are received in order.
	time.Sleep(postDeleteSleepDuration)

	return nil
}

// DecreaseTargetSize decreases the cluster node_count in magnum.
func (ng *magnumNodeGroup) DecreaseTargetSize(delta int) error {
	ng.clusterUpdateLock.Lock()
	defer ng.clusterUpdateLock.Unlock()

	if delta >= 0 {
		return fmt.Errorf("size decrease must be negative")
	}
	size := ng.targetSize
	if size+delta < ng.MinSize() {
		return fmt.Errorf("size decrease too large, desired:%d min:%d", size+delta, ng.MinSize())
	}

	klog.V(2).Infof("Decreasing target size by %d, %d->%d", delta, ng.targetSize, ng.targetSize+delta)
	err := ng.magnumManager.updateNodeCount(ng.UUID, ng.targetSize+delta)
	if err != nil {
		return fmt.Errorf("could not decrease target size: %v", err)
	}
	ng.targetSize += delta
	return nil
}

// Id returns the node group ID
func (ng *magnumNodeGroup) Id() string {
	return ng.id
}

// Debug returns a string formatted with the node group's min, max and target sizes.
func (ng *magnumNodeGroup) Debug() string {
	ng.clusterUpdateLock.Lock()
	defer ng.clusterUpdateLock.Unlock()
	return fmt.Sprintf("%s min=%d max=%d target=%d", ng.id, ng.minSize, ng.maxSize, ng.targetSize)
}

// Nodes returns a list of nodes that belong to this node group.
func (ng *magnumNodeGroup) Nodes() ([]cloudprovider.Instance, error) {
	ng.clusterUpdateLock.Lock()
	defer ng.clusterUpdateLock.Unlock()

	instances, err := ng.magnumManager.getNodes(ng.UUID)
	if err != nil {
		return nil, fmt.Errorf("could not get nodes: %v", err)
	}

	for node, deletedTime := range ng.deletedNodes {
		if time.Since(deletedTime) > 10*time.Minute {
			// Remove the node from the list of recently deleted nodes after 10 minutes.
			klog.V(3).Infof("Removing node %s from the deleted nodes cache after 10 minutes", node)
			delete(ng.deletedNodes, node)
		}
	}

	for i, instance := range instances {
		if instance.Status.State == cloudprovider.InstanceDeleting {
			continue
		}
		if deleteTime, ok := ng.deletedNodes[instance.Id]; ok {
			// This node has recently been requested for deletion, report the state as delete in progress.
			klog.V(3).Infof("Node %s has received deletetion request %s ago, reporting it as delete in progress instead of %v", instance.Id, time.Since(deleteTime), instance.Status.State)
			instances[i].Status.State = cloudprovider.InstanceDeleting
		}

	}
	return instances, nil
}

// TemplateNodeInfo returns a node template for this node group.
func (ng *magnumNodeGroup) TemplateNodeInfo() (*schedulerframework.NodeInfo, error) {
	return nil, cloudprovider.ErrNotImplemented
}

// Exist returns if this node group exists.
// Currently always returns true.
func (ng *magnumNodeGroup) Exist() bool {
	return true
}

// Create creates the node group on the cloud provider side.
func (ng *magnumNodeGroup) Create() (cloudprovider.NodeGroup, error) {
	return nil, cloudprovider.ErrAlreadyExist
}

// Delete deletes the node group on the cloud provider side.
func (ng *magnumNodeGroup) Delete() error {
	return cloudprovider.ErrNotImplemented
}

// Autoprovisioned returns if the nodegroup is autoprovisioned.
func (ng *magnumNodeGroup) Autoprovisioned() bool {
	return false
}

// MaxSize returns the maximum allowed size of the node group.
func (ng *magnumNodeGroup) MaxSize() int {
	return ng.maxSize
}

// MinSize returns the minimum allowed size of the node group.
func (ng *magnumNodeGroup) MinSize() int {
	return ng.minSize
}

// TargetSize returns the target size of the node group.
func (ng *magnumNodeGroup) TargetSize() (int, error) {
	return ng.targetSize, nil
}
<|endoftext|>


<commit_before>package fileutil_test

import (
//	"fmt"
	"github.com/APTrust/exchange/util/fileutil"
	"github.com/stretchr/testify/assert"
//	"io"
//	"os"
	"path"
	"path/filepath"
	"runtime"
//	"strings"
	"testing"
)

func TestNewTarFileIterator(t *testing.T) {
	_, filename, _, _ := runtime.Caller(0)
	tarFilePath, _ := filepath.Abs(path.Join(filepath.Dir(filename),
		"..", "..", "testdata", "example.edu.tagsample_good.tar"))
	tfi, err := fileutil.NewTarFileIterator(tarFilePath)
	assert.NotNil(t, tfi)
	assert.Nil(t, err)
}

func TestNext(t *testing.T) {

}

func TestTarFileIteratorClose(t *testing.T) {

}

func TestRead(t *testing.T) {

}

func TestTarReaderCloserClose(t *testing.T) {

}
<commit_msg>More TarFileIterator tests<commit_after>package fileutil_test

import (
//	"fmt"
	"github.com/APTrust/exchange/util/fileutil"
	"github.com/stretchr/testify/assert"
	"io"
//	"os"
	"path"
	"path/filepath"
	"runtime"
//	"strings"
	"testing"
)

func TestNewTarFileIterator(t *testing.T) {
	_, filename, _, _ := runtime.Caller(0)
	tarFilePath, _ := filepath.Abs(path.Join(filepath.Dir(filename),
		"..", "..", "testdata", "example.edu.tagsample_good.tar"))
	tfi, err := fileutil.NewTarFileIterator(tarFilePath)
	assert.NotNil(t, tfi)
	assert.Nil(t, err)
}

func TestNext(t *testing.T) {
	_, filename, _, _ := runtime.Caller(0)
	tarFilePath, _ := filepath.Abs(path.Join(filepath.Dir(filename),
		"..", "..", "testdata", "example.edu.tagsample_good.tar"))
	tfi, err := fileutil.NewTarFileIterator(tarFilePath)
	if tfi != nil {
		defer tfi.Close()
	}
	assert.NotNil(t, tfi)
	assert.Nil(t, err)

	for {
		reader, fileSummary, err := tfi.Next()
		if err == io.EOF {
			break
		}
		if reader == nil {
			assert.Fail(t, "Reader is nil")
		}
		if fileSummary == nil {
			assert.Fail(t, "FileSummary is nil")
		}

		assert.NotEmpty(t, fileSummary.Name)
		assert.Empty(t, fileSummary.AbsPath)
		assert.NotNil(t, fileSummary.Mode)
		if fileSummary.IsRegularFile {
			assert.True(t, fileSummary.Size > int64(0))
		}
		assert.False(t, fileSummary.ModTime.IsZero())

		buf := make([]byte, 1024)
		_, err = reader.Read(buf)
		if err != nil {
			assert.Equal(t, io.EOF, err)
		}
	}
}

func TestTarFileIteratorClose(t *testing.T) {

}

func TestRead(t *testing.T) {

}

func TestTarReaderCloserClose(t *testing.T) {

}
<|endoftext|>


<commit_before>package gitmediafilters

import (
	".."
	"crypto/sha256"
	"encoding/hex"
	"io"
	"os"
	"strconv"
)

type CleanedAsset struct {
	Size          int64
	File          *os.File
	Sha           string
	mediafilepath string
}

func Clean(reader io.Reader) (*CleanedAsset, error) {
	tmp, err := gitmedia.TempFile()
	if err != nil {
		return nil, err
	}

	oidHash := sha256.New()
	writer := io.MultiWriter(oidHash, tmp)
	written, err := io.Copy(writer, reader)

	return &CleanedAsset{written, tmp, hex.EncodeToString(oidHash.Sum(nil)), ""}, err
}

func (a *CleanedAsset) Close() error {
	return os.Remove(a.File.Name())
}
<commit_msg>use the file size to calculate the oid<commit_after>package gitmediafilters

import (
	".."
	"crypto/sha256"
	"encoding/hex"
	"io"
	"os"
	"strconv"
)

type CleanedAsset struct {
	Size          int64
	File          *os.File
	Sha           string
	mediafilepath string
}

func Clean(reader io.Reader) (*CleanedAsset, error) {
	tmp, err := gitmedia.TempFile()
	if err != nil {
		return nil, err
	}

	oidHash := sha256.New()
	writer := io.MultiWriter(oidHash, tmp)
	written, err := io.Copy(writer, reader)
	oidHash.Write([]byte(strconv.FormatInt(written, 10)))

	return &CleanedAsset{written, tmp, hex.EncodeToString(oidHash.Sum(nil)), ""}, err
}

func (a *CleanedAsset) Close() error {
	return os.Remove(a.File.Name())
}
<|endoftext|>


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

package manual

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"strings"

	"github.com/juju/loggo"

	coreCloudinit "launchpad.net/juju-core/cloudinit"
	"launchpad.net/juju-core/cloudinit/sshinit"
	"launchpad.net/juju-core/environs/cloudinit"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/juju"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/state/api/params"
	"launchpad.net/juju-core/tools"
	"launchpad.net/juju-core/utils"
	"launchpad.net/juju-core/utils/shell"
)

const manualInstancePrefix = "manual:"

var logger = loggo.GetLogger("juju.environs.manual")

type ProvisionMachineArgs struct {
	// Host is the SSH host: [user@]host
	Host string

	// DataDir is the root directory for juju data.
	// If left blank, the default location "/var/lib/juju" will be used.
	DataDir string

	// EnvName is the name of the environment for which the machine will be provisioned.
	EnvName string

	// Tools to install on the machine. If nil, tools will be automatically
	// chosen using environs/tools FindInstanceTools.
	Tools *tools.Tools

	// Stdin is required to respond to sudo prompts,
	// and must be a terminal (except in tests)
	Stdin io.Reader

	// Stdout is required to present sudo prompts to the user.
	Stdout io.Writer

	// Stderr is required to present machine provisioning progress to the user.
	Stderr io.Writer
}

// ErrProvisioned is returned by ProvisionMachine if the target
// machine has an existing machine agent.
var ErrProvisioned = errors.New("machine is already provisioned")

// ProvisionMachine provisions a machine agent to an existing host, via
// an SSH connection to the specified host. The host may optionally be preceded
// with a login username, as in [user@]host.
//
// On successful completion, this function will return the id of the state.Machine
// that was entered into state.
func ProvisionMachine(args ProvisionMachineArgs) (machineId string, err error) {
	client, err := juju.NewAPIClientFromName(args.EnvName)
	if err != nil {
		return "", err
	}
	defer func() {
		if machineId != "" && err != nil {
			logger.Errorf("provisioning failed, removing machine %v: %v", machineId, err)
			if cleanupErr := client.DestroyMachines(machineId); cleanupErr != nil {
				logger.Warningf("error cleaning up machine: %s", cleanupErr)
			}
			machineId = ""
		}
		client.Close()
	}()

	// Create the "ubuntu" user and initialise passwordless sudo. We populate
	// the ubuntu user's authorized_keys file with the public keys in the current
	// user's ~/.ssh directory. The authenticationworker will later update the
	// ubuntu user's authorized_keys.
	user, hostname := splitUserHost(args.Host)
	authorizedKeys, err := config.ReadAuthorizedKeys("")
	if err := InitUbuntuUser(hostname, user, authorizedKeys, args.Stdin, args.Stdout); err != nil {
		return "", err
	}

	machineParams, err := gatherMachineParams(hostname)
	if err != nil {
		return "", err
	}

	// Inform Juju that the machine exists.
	machineId, err = recordMachineInState(client, *machineParams)
	if err != nil {
		return "", err
	}

	provisioningScript, err := client.ProvisioningScript(params.ProvisioningScriptParams{
		MachineId: machineId,
		Nonce:     machineParams.Nonce,
	})
	if err != nil {
		return "", err
	}

	// Finally, provision the machine agent.
	err = runProvisionScript(provisioningScript, hostname, args.Stderr)
	if err != nil {
		return machineId, err
	}

	logger.Infof("Provisioned machine %v", machineId)
	return machineId, nil
}

func splitUserHost(host string) (string, string) {
	if at := strings.Index(host, "@"); at != -1 {
		return host[:at], host[at+1:]
	}
	return "", host
}

func recordMachineInState(
	client *api.Client, machineParams params.AddMachineParams) (machineId string, err error) {
	results, err := client.AddMachines([]params.AddMachineParams{machineParams})
	if err != nil {
		return "", err
	}
	// Currently, only one machine is added, but in future there may be several added in one call.
	machineInfo := results[0]
	if machineInfo.Error != nil {
		return "", machineInfo.Error
	}
	return machineInfo.Machine, nil
}

// convertToStateJobs takes a slice of params.MachineJob and makes them a slice of state.MachineJob
func convertToStateJobs(jobs []params.MachineJob) ([]state.MachineJob, error) {
	outJobs := make([]state.MachineJob, len(jobs))
	var err error
	for j, job := range jobs {
		if outJobs[j], err = state.MachineJobFromParams(job); err != nil {
			return nil, err
		}
	}
	return outJobs, nil
}

// gatherMachineParams collects all the information we know about the machine
// we are about to provision. It will SSH into that machine as the ubuntu user.
// The hostname supplied should not include a username.
// If we can, we will reverse lookup the hostname by its IP address, and use
// the DNS resolved name, rather than the name that was supplied
func gatherMachineParams(hostname string) (*params.AddMachineParams, error) {

	// Generate a unique nonce for the machine.
	uuid, err := utils.NewUUID()
	if err != nil {
		return nil, err
	}

	var addrs []instance.Address
	if addr, err := HostAddress(hostname); err != nil {
		logger.Warningf("failed to compute public address for %q: %v", hostname, err)
	} else {
		addrs = append(addrs, addr)
	}

	provisioned, err := checkProvisioned(hostname)
	if err != nil {
		err = fmt.Errorf("error checking if provisioned: %v", err)
		return nil, err
	}
	if provisioned {
		return nil, ErrProvisioned
	}

	hc, series, err := DetectSeriesAndHardwareCharacteristics(hostname)
	if err != nil {
		err = fmt.Errorf("error detecting hardware characteristics: %v", err)
		return nil, err
	}

	// There will never be a corresponding "instance" that any provider
	// knows about. This is fine, and works well with the provisioner
	// task. The provisioner task will happily remove any and all dead
	// machines from state, but will ignore the associated instance ID
	// if it isn't one that the environment provider knows about.

	instanceId := instance.Id(manualInstancePrefix + hostname)
	nonce := fmt.Sprintf("%s:%s", instanceId, uuid.String())
	machineParams := &params.AddMachineParams{
		Series:                  series,
		HardwareCharacteristics: hc,
		InstanceId:              instanceId,
		Nonce:                   nonce,
		Addrs:                   addrs,
		Jobs:                    []params.MachineJob{params.JobHostUnits},
	}
	return machineParams, nil
}

var provisionMachineAgent = func(host string, mcfg *cloudinit.MachineConfig, progressWriter io.Writer) error {
	script, err := ProvisioningScript(mcfg)
	if err != nil {
		return err
	}
	return runProvisionScript(script, host, progressWriter)
}

// ProvisioningScript generates a bash script that can be
// executed on a remote host to carry out the cloud-init
// configuration.
func ProvisioningScript(mcfg *cloudinit.MachineConfig) (string, error) {
	cloudcfg := coreCloudinit.New()
	if err := cloudinit.ConfigureJuju(mcfg, cloudcfg); err != nil {
		return "", err
	}
	// Explicitly disabling apt_upgrade so as not to trample
	// the target machine's existing configuration.
	cloudcfg.SetAptUpgrade(false)
	configScript, err := sshinit.ConfigureScript(cloudcfg)
	if err != nil {
		return "", err
	}

	var buf bytes.Buffer
	// Always remove the cloud-init-output.log file first, if it exists.
	fmt.Fprintf(&buf, "rm -f %s\n", utils.ShQuote(mcfg.CloudInitOutputLog))
	// If something goes wrong, dump cloud-init-output.log to stderr.
	buf.WriteString(shell.DumpFileOnErrorScript(mcfg.CloudInitOutputLog))
	buf.WriteString(configScript)
	return buf.String(), nil
}

func runProvisionScript(script, host string, progressWriter io.Writer) error {
	params := sshinit.ConfigureParams{
		Host:           "ubuntu@" + host,
		ProgressWriter: progressWriter,
	}
	return sshinit.RunConfigureScript(script, params)
}
<commit_msg>environs/manual: use old AddMAchines API<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package manual

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"strings"

	"github.com/juju/loggo"

	coreCloudinit "launchpad.net/juju-core/cloudinit"
	"launchpad.net/juju-core/cloudinit/sshinit"
	"launchpad.net/juju-core/environs/cloudinit"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/juju"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/state/api/params"
	"launchpad.net/juju-core/tools"
	"launchpad.net/juju-core/utils"
	"launchpad.net/juju-core/utils/shell"
)

const manualInstancePrefix = "manual:"

var logger = loggo.GetLogger("juju.environs.manual")

type ProvisionMachineArgs struct {
	// Host is the SSH host: [user@]host
	Host string

	// DataDir is the root directory for juju data.
	// If left blank, the default location "/var/lib/juju" will be used.
	DataDir string

	// EnvName is the name of the environment for which the machine will be provisioned.
	EnvName string

	// Tools to install on the machine. If nil, tools will be automatically
	// chosen using environs/tools FindInstanceTools.
	Tools *tools.Tools

	// Stdin is required to respond to sudo prompts,
	// and must be a terminal (except in tests)
	Stdin io.Reader

	// Stdout is required to present sudo prompts to the user.
	Stdout io.Writer

	// Stderr is required to present machine provisioning progress to the user.
	Stderr io.Writer
}

// ErrProvisioned is returned by ProvisionMachine if the target
// machine has an existing machine agent.
var ErrProvisioned = errors.New("machine is already provisioned")

// ProvisionMachine provisions a machine agent to an existing host, via
// an SSH connection to the specified host. The host may optionally be preceded
// with a login username, as in [user@]host.
//
// On successful completion, this function will return the id of the state.Machine
// that was entered into state.
func ProvisionMachine(args ProvisionMachineArgs) (machineId string, err error) {
	client, err := juju.NewAPIClientFromName(args.EnvName)
	if err != nil {
		return "", err
	}
	defer func() {
		if machineId != "" && err != nil {
			logger.Errorf("provisioning failed, removing machine %v: %v", machineId, err)
			if cleanupErr := client.DestroyMachines(machineId); cleanupErr != nil {
				logger.Warningf("error cleaning up machine: %s", cleanupErr)
			}
			machineId = ""
		}
		client.Close()
	}()

	// Create the "ubuntu" user and initialise passwordless sudo. We populate
	// the ubuntu user's authorized_keys file with the public keys in the current
	// user's ~/.ssh directory. The authenticationworker will later update the
	// ubuntu user's authorized_keys.
	user, hostname := splitUserHost(args.Host)
	authorizedKeys, err := config.ReadAuthorizedKeys("")
	if err := InitUbuntuUser(hostname, user, authorizedKeys, args.Stdin, args.Stdout); err != nil {
		return "", err
	}

	machineParams, err := gatherMachineParams(hostname)
	if err != nil {
		return "", err
	}

	// Inform Juju that the machine exists.
	machineId, err = recordMachineInState(client, *machineParams)
	if err != nil {
		return "", err
	}

	provisioningScript, err := client.ProvisioningScript(params.ProvisioningScriptParams{
		MachineId: machineId,
		Nonce:     machineParams.Nonce,
	})
	if err != nil {
		return "", err
	}

	// Finally, provision the machine agent.
	err = runProvisionScript(provisioningScript, hostname, args.Stderr)
	if err != nil {
		return machineId, err
	}

	logger.Infof("Provisioned machine %v", machineId)
	return machineId, nil
}

func splitUserHost(host string) (string, string) {
	if at := strings.Index(host, "@"); at != -1 {
		return host[:at], host[at+1:]
	}
	return "", host
}

func recordMachineInState(
	client *api.Client, machineParams params.AddMachineParams) (machineId string, err error) {
	results, err := client.AddMachines1dot18([]params.AddMachineParams{machineParams})
	if err != nil {
		return "", err
	}
	// Currently, only one machine is added, but in future there may be several added in one call.
	machineInfo := results[0]
	if machineInfo.Error != nil {
		return "", machineInfo.Error
	}
	return machineInfo.Machine, nil
}

// convertToStateJobs takes a slice of params.MachineJob and makes them a slice of state.MachineJob
func convertToStateJobs(jobs []params.MachineJob) ([]state.MachineJob, error) {
	outJobs := make([]state.MachineJob, len(jobs))
	var err error
	for j, job := range jobs {
		if outJobs[j], err = state.MachineJobFromParams(job); err != nil {
			return nil, err
		}
	}
	return outJobs, nil
}

// gatherMachineParams collects all the information we know about the machine
// we are about to provision. It will SSH into that machine as the ubuntu user.
// The hostname supplied should not include a username.
// If we can, we will reverse lookup the hostname by its IP address, and use
// the DNS resolved name, rather than the name that was supplied
func gatherMachineParams(hostname string) (*params.AddMachineParams, error) {

	// Generate a unique nonce for the machine.
	uuid, err := utils.NewUUID()
	if err != nil {
		return nil, err
	}

	var addrs []instance.Address
	if addr, err := HostAddress(hostname); err != nil {
		logger.Warningf("failed to compute public address for %q: %v", hostname, err)
	} else {
		addrs = append(addrs, addr)
	}

	provisioned, err := checkProvisioned(hostname)
	if err != nil {
		err = fmt.Errorf("error checking if provisioned: %v", err)
		return nil, err
	}
	if provisioned {
		return nil, ErrProvisioned
	}

	hc, series, err := DetectSeriesAndHardwareCharacteristics(hostname)
	if err != nil {
		err = fmt.Errorf("error detecting hardware characteristics: %v", err)
		return nil, err
	}

	// There will never be a corresponding "instance" that any provider
	// knows about. This is fine, and works well with the provisioner
	// task. The provisioner task will happily remove any and all dead
	// machines from state, but will ignore the associated instance ID
	// if it isn't one that the environment provider knows about.

	instanceId := instance.Id(manualInstancePrefix + hostname)
	nonce := fmt.Sprintf("%s:%s", instanceId, uuid.String())
	machineParams := &params.AddMachineParams{
		Series:                  series,
		HardwareCharacteristics: hc,
		InstanceId:              instanceId,
		Nonce:                   nonce,
		Addrs:                   addrs,
		Jobs:                    []params.MachineJob{params.JobHostUnits},
	}
	return machineParams, nil
}

var provisionMachineAgent = func(host string, mcfg *cloudinit.MachineConfig, progressWriter io.Writer) error {
	script, err := ProvisioningScript(mcfg)
	if err != nil {
		return err
	}
	return runProvisionScript(script, host, progressWriter)
}

// ProvisioningScript generates a bash script that can be
// executed on a remote host to carry out the cloud-init
// configuration.
func ProvisioningScript(mcfg *cloudinit.MachineConfig) (string, error) {
	cloudcfg := coreCloudinit.New()
	if err := cloudinit.ConfigureJuju(mcfg, cloudcfg); err != nil {
		return "", err
	}
	// Explicitly disabling apt_upgrade so as not to trample
	// the target machine's existing configuration.
	cloudcfg.SetAptUpgrade(false)
	configScript, err := sshinit.ConfigureScript(cloudcfg)
	if err != nil {
		return "", err
	}

	var buf bytes.Buffer
	// Always remove the cloud-init-output.log file first, if it exists.
	fmt.Fprintf(&buf, "rm -f %s\n", utils.ShQuote(mcfg.CloudInitOutputLog))
	// If something goes wrong, dump cloud-init-output.log to stderr.
	buf.WriteString(shell.DumpFileOnErrorScript(mcfg.CloudInitOutputLog))
	buf.WriteString(configScript)
	return buf.String(), nil
}

func runProvisionScript(script, host string, progressWriter io.Writer) error {
	params := sshinit.ConfigureParams{
		Host:           "ubuntu@" + host,
		ProgressWriter: progressWriter,
	}
	return sshinit.RunConfigureScript(script, params)
}
<|endoftext|>


<commit_before>package instana

import (
	"os"
	"path/filepath"
)

const (
	DefaultMaxBufferedSpans = 1000
	DefaultForceSpanSendAt  = 500
)

type sensorS struct {
	meter       *meterS
	agent       *agentS
	options     *Options
	serviceName string
}

var sensor *sensorS

func (r *sensorS) init(options *Options) {
	// sensor can be initialized explicitly or implicitly through OpenTracing global init
	if r.meter == nil {
		r.setOptions(options)
		r.configureServiceName()
		r.agent = r.initAgent()
		r.meter = r.initMeter()
	}
}

func (r *sensorS) setOptions(options *Options) {
	r.options = options
	if r.options == nil {
		r.options = &Options{}
	}

	if r.options.MaxBufferedSpans == 0 {
		r.options.MaxBufferedSpans = DefaultMaxBufferedSpans
	}

	if r.options.ForceTransmissionStartingAt == 0 {
		r.options.ForceTransmissionStartingAt = DefaultForceSpanSendAt
	}
}

func (r *sensorS) getOptions() *Options {
	return r.options
}

func (r *sensorS) configureServiceName() {
	if r.options != nil {
		r.serviceName = r.options.Service
	}

	if r.serviceName == "" {
		r.serviceName = filepath.Base(os.Args[0])
	}
}

// InitSensor intializes the sensor (without tracing) to begin collecting
// and reporting metrics.
func InitSensor(options *Options) {
	if sensor == nil {
		sensor = new(sensorS)
		// If this environment variable is set, then override log level
		_, ok := os.LookupEnv("INSTANA_DEBUG")
		if ok {
			options.LogLevel = Debug
		}

		sensor.initLog()
		sensor.init(options)
		log.debug("initialized sensor")
	}
}
<commit_msg>Return early from instana.InitSensor() if sensor has already been initialized<commit_after>package instana

import (
	"os"
	"path/filepath"
)

const (
	DefaultMaxBufferedSpans = 1000
	DefaultForceSpanSendAt  = 500
)

type sensorS struct {
	meter       *meterS
	agent       *agentS
	options     *Options
	serviceName string
}

var sensor *sensorS

func (r *sensorS) init(options *Options) {
	// sensor can be initialized explicitly or implicitly through OpenTracing global init
	if r.meter == nil {
		r.setOptions(options)
		r.configureServiceName()
		r.agent = r.initAgent()
		r.meter = r.initMeter()
	}
}

func (r *sensorS) setOptions(options *Options) {
	r.options = options
	if r.options == nil {
		r.options = &Options{}
	}

	if r.options.MaxBufferedSpans == 0 {
		r.options.MaxBufferedSpans = DefaultMaxBufferedSpans
	}

	if r.options.ForceTransmissionStartingAt == 0 {
		r.options.ForceTransmissionStartingAt = DefaultForceSpanSendAt
	}
}

func (r *sensorS) getOptions() *Options {
	return r.options
}

func (r *sensorS) configureServiceName() {
	if r.options != nil {
		r.serviceName = r.options.Service
	}

	if r.serviceName == "" {
		r.serviceName = filepath.Base(os.Args[0])
	}
}

// InitSensor intializes the sensor (without tracing) to begin collecting
// and reporting metrics.
func InitSensor(options *Options) {
	if sensor != nil {
		return
	}

	sensor = &sensorS{}

	// If this environment variable is set, then override log level
	_, ok := os.LookupEnv("INSTANA_DEBUG")
	if ok {
		options.LogLevel = Debug
	}

	sensor.initLog()
	sensor.init(options)
	log.debug("initialized sensor")
}
<|endoftext|>


<commit_before>/*
Gomega's format package pretty-prints objects.  It explores input objects recursively and generates formatted, indented output with type information.
*/
package format

import (
	"fmt"
	"reflect"
	"strings"
)

// Use MaxDepth to set the maximum recursion depth when printing deeply nested objects
var MaxDepth = uint(10)

/*
By default, all objects (even those that implement fmt.Stringer and fmt.GoStringer) are recursively inspected to generate output.

Set UseStringerRepresentation = true to use GoString (for fmt.GoStringers) or String (for fmt.Stringer) instead.

Note that GoString and String don't always have all the information you need to understand why a test failed!
*/
var UseStringerRepresentation = false

//The default indentation string emitted by the format package
var Indent = "    "

var longFormThreshold = 20

/*
Generates a formatted matcher success/failure message of the form:

	Expected
		<pretty printed actual>
	<message>
		<pretty printed expected>

If expected is omited, then the message looks like:

	Expected
		<pretty printed actual>
	<message>
*/
func Message(actual interface{}, message string, expected ...interface{}) string {
	if len(expected) == 0 {
		return fmt.Sprintf("Expected\n%s\n%s", Object(actual, 1), message)
	} else {
		return fmt.Sprintf("Expected\n%s\n%s\n%s", Object(actual, 1), message, Object(expected[0], 1))
	}
}

/*
Pretty prints the passed in object at the passed in indentation level.

Object recurses into deeply nested objects emitting pretty-printed representations of their components.

Modify format.MaxDepth to control how deep the recursion is allowed to go
Set format.UseStringerRepresentation to true to return object.GoString() or object.String() when available instead of
recursing into the object.
*/
func Object(object interface{}, indentation uint) string {
	indent := strings.Repeat(Indent, int(indentation))
	value := reflect.ValueOf(object)
	return fmt.Sprintf("%s<%s>: %s", indent, formatType(object), formatValue(value, indentation))
}

/*
IndentString takes a string and indents each line by the specified amount.
*/
func IndentString(s string, indentation uint) string {
	components := strings.Split(s, "\n")
	result := ""
	indent := strings.Repeat(Indent, int(indentation))
	for i, component := range components {
		result += indent + component
		if i < len(components)-1 {
			result += "\n"
		}
	}

	return result
}

func formatType(object interface{}) string {
	t := reflect.TypeOf(object)
	if t == nil {
		return "nil"
	}
	switch t.Kind() {
	case reflect.Chan:
		v := reflect.ValueOf(object)
		return fmt.Sprintf("%T | len:%d, cap:%d", object, v.Len(), v.Cap())
	case reflect.Ptr:
		return fmt.Sprintf("%T | %p", object, object)
	case reflect.Slice:
		v := reflect.ValueOf(object)
		return fmt.Sprintf("%T | len:%d, cap:%d", object, v.Len(), v.Cap())
	case reflect.Map:
		v := reflect.ValueOf(object)
		return fmt.Sprintf("%T | len:%d", object, v.Len())
	default:
		return fmt.Sprintf("%T", object)
	}
}

func formatValue(value reflect.Value, indentation uint) string {
	if indentation > MaxDepth {
		return "..."
	}

	if isNilValue(value) {
		return "nil"
	}

	if UseStringerRepresentation {
		if value.CanInterface() {
			obj := value.Interface()
			switch x := obj.(type) {
			case fmt.GoStringer:
				return x.GoString()
			case fmt.Stringer:
				return x.String()
			}
		}
	}

	switch value.Kind() {
	case reflect.Bool:
		return fmt.Sprintf("%v", value.Bool())
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return fmt.Sprintf("%v", value.Int())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return fmt.Sprintf("%v", value.Uint())
	case reflect.Uintptr:
		return fmt.Sprintf("0x%x", value.Uint())
	case reflect.Float32, reflect.Float64:
		return fmt.Sprintf("%v", value.Float())
	case reflect.Complex64, reflect.Complex128:
		return fmt.Sprintf("%v", value.Complex())
	case reflect.Chan:
		return fmt.Sprintf("0x%x", value.Pointer())
	case reflect.Func:
		return fmt.Sprintf("0x%x", value.Pointer())
	case reflect.Ptr:
		return formatValue(value.Elem(), indentation)
	case reflect.Slice:
		if value.Type().Elem().Kind() == reflect.Uint8 {
			return formatString(value.Bytes(), indentation)
		}
		return formatSlice(value, indentation)
	case reflect.String:
		return formatString(value.String(), indentation)
	case reflect.Array:
		return formatSlice(value, indentation)
	case reflect.Map:
		return formatMap(value, indentation)
	case reflect.Struct:
		return formatStruct(value, indentation)
	case reflect.Interface:
		return formatValue(value.Elem(), indentation)
	default:
		if value.CanInterface() {
			return fmt.Sprintf("%#v", value.Interface())
		} else {
			return fmt.Sprintf("%#v", value)
		}
	}
}

func formatString(object interface{}, indentation uint) string {
	if indentation == 1 {
		s := fmt.Sprintf("%s", object)
		components := strings.Split(s, "\n")
		result := ""
		for i, component := range components {
			if i == 0 {
				result += component
			} else {
				result += Indent + component
			}
			if i < len(components)-1 {
				result += "\n"
			}
		}

		return fmt.Sprintf("%s", result)
	} else {
		return fmt.Sprintf("%q", object)
	}
}

func formatSlice(v reflect.Value, indentation uint) string {
	l := v.Len()
	result := make([]string, l)
	longest := 0
	for i := 0; i < l; i++ {
		result[i] = formatValue(v.Index(i), indentation+1)
		if len(result[i]) > longest {
			longest = len(result[i])
		}
	}

	if longest > longFormThreshold {
		indenter := strings.Repeat(Indent, int(indentation))
		return fmt.Sprintf("[\n%s%s,\n%s]", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter)
	} else {
		return fmt.Sprintf("[%s]", strings.Join(result, ", "))
	}
}

func formatMap(v reflect.Value, indentation uint) string {
	l := v.Len()
	result := make([]string, l)

	longest := 0
	for i, key := range v.MapKeys() {
		value := v.MapIndex(key)
		result[i] = fmt.Sprintf("%s: %s", formatValue(key, 0), formatValue(value, indentation+1))
		if len(result[i]) > longest {
			longest = len(result[i])
		}
	}

	if longest > longFormThreshold {
		indenter := strings.Repeat(Indent, int(indentation))
		return fmt.Sprintf("{\n%s%s,\n%s}", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter)
	} else {
		return fmt.Sprintf("{%s}", strings.Join(result, ", "))
	}
}

func formatStruct(v reflect.Value, indentation uint) string {
	t := v.Type()

	l := v.NumField()
	result := []string{}
	longest := 0
	for i := 0; i < l; i++ {
		structField := t.Field(i)
		fieldEntry := v.Field(i)
		representation := fmt.Sprintf("%s: %s", structField.Name, formatValue(fieldEntry, indentation+1))
		result = append(result, representation)
		if len(representation) > longest {
			longest = len(representation)
		}
	}
	if longest > longFormThreshold {
		indenter := strings.Repeat(Indent, int(indentation))
		return fmt.Sprintf("{\n%s%s,\n%s}", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter)
	} else {
		return fmt.Sprintf("{%s}", strings.Join(result, ", "))
	}
}

func isNilValue(a reflect.Value) bool {
	switch a.Kind() {
	case reflect.Invalid:
		return true
	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
		return a.IsNil()
	}

	return false
}
<commit_msg>Always escape a slice of bytes<commit_after>/*
Gomega's format package pretty-prints objects.  It explores input objects recursively and generates formatted, indented output with type information.
*/
package format

import (
	"fmt"
	"reflect"
	"strings"
)

// Use MaxDepth to set the maximum recursion depth when printing deeply nested objects
var MaxDepth = uint(10)

/*
By default, all objects (even those that implement fmt.Stringer and fmt.GoStringer) are recursively inspected to generate output.

Set UseStringerRepresentation = true to use GoString (for fmt.GoStringers) or String (for fmt.Stringer) instead.

Note that GoString and String don't always have all the information you need to understand why a test failed!
*/
var UseStringerRepresentation = false

//The default indentation string emitted by the format package
var Indent = "    "

var longFormThreshold = 20

/*
Generates a formatted matcher success/failure message of the form:

	Expected
		<pretty printed actual>
	<message>
		<pretty printed expected>

If expected is omited, then the message looks like:

	Expected
		<pretty printed actual>
	<message>
*/
func Message(actual interface{}, message string, expected ...interface{}) string {
	if len(expected) == 0 {
		return fmt.Sprintf("Expected\n%s\n%s", Object(actual, 1), message)
	} else {
		return fmt.Sprintf("Expected\n%s\n%s\n%s", Object(actual, 1), message, Object(expected[0], 1))
	}
}

/*
Pretty prints the passed in object at the passed in indentation level.

Object recurses into deeply nested objects emitting pretty-printed representations of their components.

Modify format.MaxDepth to control how deep the recursion is allowed to go
Set format.UseStringerRepresentation to true to return object.GoString() or object.String() when available instead of
recursing into the object.
*/
func Object(object interface{}, indentation uint) string {
	indent := strings.Repeat(Indent, int(indentation))
	value := reflect.ValueOf(object)
	return fmt.Sprintf("%s<%s>: %s", indent, formatType(object), formatValue(value, indentation))
}

/*
IndentString takes a string and indents each line by the specified amount.
*/
func IndentString(s string, indentation uint) string {
	components := strings.Split(s, "\n")
	result := ""
	indent := strings.Repeat(Indent, int(indentation))
	for i, component := range components {
		result += indent + component
		if i < len(components)-1 {
			result += "\n"
		}
	}

	return result
}

func formatType(object interface{}) string {
	t := reflect.TypeOf(object)
	if t == nil {
		return "nil"
	}
	switch t.Kind() {
	case reflect.Chan:
		v := reflect.ValueOf(object)
		return fmt.Sprintf("%T | len:%d, cap:%d", object, v.Len(), v.Cap())
	case reflect.Ptr:
		return fmt.Sprintf("%T | %p", object, object)
	case reflect.Slice:
		v := reflect.ValueOf(object)
		return fmt.Sprintf("%T | len:%d, cap:%d", object, v.Len(), v.Cap())
	case reflect.Map:
		v := reflect.ValueOf(object)
		return fmt.Sprintf("%T | len:%d", object, v.Len())
	default:
		return fmt.Sprintf("%T", object)
	}
}

func formatValue(value reflect.Value, indentation uint) string {
	if indentation > MaxDepth {
		return "..."
	}

	if isNilValue(value) {
		return "nil"
	}

	if UseStringerRepresentation {
		if value.CanInterface() {
			obj := value.Interface()
			switch x := obj.(type) {
			case fmt.GoStringer:
				return x.GoString()
			case fmt.Stringer:
				return x.String()
			}
		}
	}

	switch value.Kind() {
	case reflect.Bool:
		return fmt.Sprintf("%v", value.Bool())
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return fmt.Sprintf("%v", value.Int())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return fmt.Sprintf("%v", value.Uint())
	case reflect.Uintptr:
		return fmt.Sprintf("0x%x", value.Uint())
	case reflect.Float32, reflect.Float64:
		return fmt.Sprintf("%v", value.Float())
	case reflect.Complex64, reflect.Complex128:
		return fmt.Sprintf("%v", value.Complex())
	case reflect.Chan:
		return fmt.Sprintf("0x%x", value.Pointer())
	case reflect.Func:
		return fmt.Sprintf("0x%x", value.Pointer())
	case reflect.Ptr:
		return formatValue(value.Elem(), indentation)
	case reflect.Slice:
		return formatSlice(value, indentation)
	case reflect.String:
		return formatString(value.String(), indentation)
	case reflect.Array:
		return formatSlice(value, indentation)
	case reflect.Map:
		return formatMap(value, indentation)
	case reflect.Struct:
		return formatStruct(value, indentation)
	case reflect.Interface:
		return formatValue(value.Elem(), indentation)
	default:
		if value.CanInterface() {
			return fmt.Sprintf("%#v", value.Interface())
		} else {
			return fmt.Sprintf("%#v", value)
		}
	}
}

func formatString(object interface{}, indentation uint) string {
	if indentation == 1 {
		s := fmt.Sprintf("%s", object)
		components := strings.Split(s, "\n")
		result := ""
		for i, component := range components {
			if i == 0 {
				result += component
			} else {
				result += Indent + component
			}
			if i < len(components)-1 {
				result += "\n"
			}
		}

		return fmt.Sprintf("%s", result)
	} else {
		return fmt.Sprintf("%q", object)
	}
}

func formatSlice(v reflect.Value, indentation uint) string {
	l := v.Len()
	result := make([]string, l)
	longest := 0
	for i := 0; i < l; i++ {
		result[i] = formatValue(v.Index(i), indentation+1)
		if len(result[i]) > longest {
			longest = len(result[i])
		}
	}

	if longest > longFormThreshold {
		indenter := strings.Repeat(Indent, int(indentation))
		return fmt.Sprintf("[\n%s%s,\n%s]", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter)
	} else {
		return fmt.Sprintf("[%s]", strings.Join(result, ", "))
	}
}

func formatMap(v reflect.Value, indentation uint) string {
	l := v.Len()
	result := make([]string, l)

	longest := 0
	for i, key := range v.MapKeys() {
		value := v.MapIndex(key)
		result[i] = fmt.Sprintf("%s: %s", formatValue(key, 0), formatValue(value, indentation+1))
		if len(result[i]) > longest {
			longest = len(result[i])
		}
	}

	if longest > longFormThreshold {
		indenter := strings.Repeat(Indent, int(indentation))
		return fmt.Sprintf("{\n%s%s,\n%s}", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter)
	} else {
		return fmt.Sprintf("{%s}", strings.Join(result, ", "))
	}
}

func formatStruct(v reflect.Value, indentation uint) string {
	t := v.Type()

	l := v.NumField()
	result := []string{}
	longest := 0
	for i := 0; i < l; i++ {
		structField := t.Field(i)
		fieldEntry := v.Field(i)
		representation := fmt.Sprintf("%s: %s", structField.Name, formatValue(fieldEntry, indentation+1))
		result = append(result, representation)
		if len(representation) > longest {
			longest = len(representation)
		}
	}
	if longest > longFormThreshold {
		indenter := strings.Repeat(Indent, int(indentation))
		return fmt.Sprintf("{\n%s%s,\n%s}", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter)
	} else {
		return fmt.Sprintf("{%s}", strings.Join(result, ", "))
	}
}

func isNilValue(a reflect.Value) bool {
	switch a.Kind() {
	case reflect.Invalid:
		return true
	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
		return a.IsNil()
	}

	return false
}
<|endoftext|>