<commit_before>package service

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	docker "github.com/fsouza/go-dockerclient"
	logging "github.com/op/go-logging"
	"github.com/pkg/errors"
)

const (
	stopContainerTimeout = 60 // Seconds before a container is killed (after graceful stop)
	containerFileName    = "CONTAINER"
)

// NewDockerRunner creates a runner that starts processes on the local OS.
func NewDockerRunner(log *logging.Logger, endpoint, image, user, volumesFrom string, gcDelay time.Duration, networkMode string, privileged bool) (Runner, error) {
	client, err := docker.NewClient(endpoint)
	if err != nil {
		return nil, maskAny(err)
	}
	return &dockerRunner{
		log:          log,
		client:       client,
		image:        image,
		user:         user,
		volumesFrom:  volumesFrom,
		containerIDs: make(map[string]time.Time),
		gcDelay:      gcDelay,
		networkMode:  networkMode,
		privileged:   privileged,
	}, nil
}

// dockerRunner implements a Runner that starts processes in a docker container.
type dockerRunner struct {
	log          *logging.Logger
	client       *docker.Client
	image        string
	user         string
	volumesFrom  string
	mutex        sync.Mutex
	containerIDs map[string]time.Time
	gcOnce       sync.Once
	gcDelay      time.Duration
	networkMode  string
	privileged   bool
}

type dockerContainer struct {
	client    *docker.Client
	container *docker.Container
}

func (r *dockerRunner) GetContainerDir(hostDir string) string {
	if r.volumesFrom != "" {
		return hostDir
	}
	return "/data"
}

// GetRunningServer checks if there is already a server process running in the given server directory.
// If that is the case, its process is returned.
// Otherwise nil is returned.
func (r *dockerRunner) GetRunningServer(serverDir string) (Process, error) {
	containerContent, err := ioutil.ReadFile(filepath.Join(serverDir, containerFileName))
	if os.IsNotExist(err) {
		return nil, nil
	} else if err != nil {
		return nil, maskAny(err)
	}
	id := string(containerContent)
	// We found a CONTAINER file, see if this container is still running
	c, err := r.client.InspectContainer(id)
	if err != nil {
		// Container cannot be inspected, assume it no longer exists
		return nil, nil
	}
	// Container can be inspected, check its state
	if !c.State.Running {
		// Container is not running
		return nil, nil
	}
	r.recordContainerID(c.ID)
	// Start gc (once)
	r.startGC()

	// Return container
	return &dockerContainer{
		client:    r.client,
		container: c,
	}, nil
}

func (r *dockerRunner) Start(command string, args []string, volumes []Volume, ports []int, containerName, serverDir string) (Process, error) {
	// Start gc (once)
	r.startGC()

	// Pull docker image
	if err := r.pullImage(r.image); err != nil {
		return nil, maskAny(err)
	}

	// Ensure container name is valid
	containerName = strings.Replace(containerName, ":", "", -1)

	var result Process
	op := func() error {
		// Make sure the container is really gone
		r.log.Debugf("Removing container '%s' (if it exists)", containerName)
		if err := r.client.RemoveContainer(docker.RemoveContainerOptions{
			ID:    containerName,
			Force: true,
		}); err != nil && !isNoSuchContainer(err) {
			r.log.Errorf("Failed to remove container '%s': %v", containerName, err)
		}
		// Try starting it now
		p, err := r.start(command, args, volumes, ports, containerName, serverDir)
		if err != nil {
			return maskAny(err)
		}
		result = p
		return nil
	}

	if err := retry(op, time.Minute*2); err != nil {
		return nil, maskAny(err)
	}
	return result, nil
}

// startGC ensures GC is started (only once)
func (r *dockerRunner) startGC() {
	// Start gc (once)
	r.gcOnce.Do(func() { go r.gc() })
}

// Try to start a command with given arguments
func (r *dockerRunner) start(command string, args []string, volumes []Volume, ports []int, containerName, serverDir string) (Process, error) {
	opts := docker.CreateContainerOptions{
		Name: containerName,
		Config: &docker.Config{
			Image:        r.image,
			Entrypoint:   []string{command},
			Cmd:          args,
			Tty:          true,
			User:         r.user,
			ExposedPorts: make(map[docker.Port]struct{}),
		},
		HostConfig: &docker.HostConfig{
			PortBindings:    make(map[docker.Port][]docker.PortBinding),
			PublishAllPorts: false,
			AutoRemove:      false,
			Privileged:      r.privileged,
		},
	}
	if r.volumesFrom != "" {
		opts.HostConfig.VolumesFrom = []string{r.volumesFrom}
	} else {
		for _, v := range volumes {
			bind := fmt.Sprintf("%s:%s", v.HostPath, v.ContainerPath)
			if v.ReadOnly {
				bind = bind + ":ro"
			}
			opts.HostConfig.Binds = append(opts.HostConfig.Binds, bind)
		}
	}
	if r.networkMode != "" && r.networkMode != "default" {
		opts.HostConfig.NetworkMode = r.networkMode
	} else {
		for _, p := range ports {
			dockerPort := docker.Port(fmt.Sprintf("%d/tcp", p))
			opts.Config.ExposedPorts[dockerPort] = struct{}{}
			opts.HostConfig.PortBindings[dockerPort] = []docker.PortBinding{
				docker.PortBinding{
					HostIP:   "0.0.0.0",
					HostPort: strconv.Itoa(p),
				},
			}
		}
	}
	r.log.Debugf("Creating container %s", containerName)
	c, err := r.client.CreateContainer(opts)
	if err != nil {
		return nil, maskAny(err)
	}
	r.recordContainerID(c.ID) // Record ID so we can clean it up later
	r.log.Debugf("Starting container %s", containerName)
	if err := r.client.StartContainer(c.ID, opts.HostConfig); err != nil {
		return nil, maskAny(err)
	}
	r.log.Debugf("Started container %s", containerName)
	// Write container ID to disk
	containerFilePath := filepath.Join(serverDir, containerFileName)
	if err := ioutil.WriteFile(containerFilePath, []byte(c.ID), 0755); err != nil {
		r.log.Errorf("Failed to store container ID in '%s': %v", containerFilePath, err)
	}
	// Inspect container to make sure we have the latest info
	c, err = r.client.InspectContainer(c.ID)
	if err != nil {
		return nil, maskAny(err)
	}
	return &dockerContainer{
		client:    r.client,
		container: c,
	}, nil
}

// pullImage tries to pull the given image.
// It retries several times upon failure.
func (r *dockerRunner) pullImage(image string) error {
	// Pull docker image
	repo, tag := docker.ParseRepositoryTag(r.image)

	op := func() error {
		r.log.Debugf("Pulling image %s:%s", repo, tag)
		if err := r.client.PullImage(docker.PullImageOptions{
			Repository: repo,
			Tag:        tag,
		}, docker.AuthConfiguration{}); err != nil {
			if isNotFound(err) {
				return maskAny(&PermanentError{err})
			}
			return maskAny(err)
		}
		return nil
	}

	if err := retry(op, time.Minute*2); err != nil {
		return maskAny(err)
	}
	return nil
}

func (r *dockerRunner) CreateStartArangodbCommand(index int, masterIP string, masterPort string) string {
	addr := masterIP
	hostPort := 4000 + (portOffsetIncrement * (index - 1))
	if masterPort != "" {
		addr = net.JoinHostPort(addr, masterPort)
		masterPortI, _ := strconv.Atoi(masterPort)
		hostPort = masterPortI + (portOffsetIncrement * (index - 1))
	}
	var netArgs string
	if r.networkMode == "" || r.networkMode == "default" {
		netArgs = fmt.Sprintf("-p %d:4000", hostPort)
	} else {
		netArgs = fmt.Sprintf("--net=%s", r.networkMode)
	}
	lines := []string{
		fmt.Sprintf("docker volume create arangodb%d &&", index),
		fmt.Sprintf("docker run -it --name=adb%d --rm %s -v arangodb%d:/data", index, netArgs, index),
		fmt.Sprintf("-v /var/run/docker.sock:/var/run/docker.sock arangodb/arangodb-starter"),
		fmt.Sprintf("--dockerContainer=adb%d --ownAddress=%s --join=%s", index, masterIP, addr),
	}
	return strings.Join(lines, " \\\n    ")
}

// Cleanup after all processes are dead and have been cleaned themselves
func (r *dockerRunner) Cleanup() error {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	for id := range r.containerIDs {
		r.log.Infof("Removing container %s", id)
		if err := r.client.RemoveContainer(docker.RemoveContainerOptions{
			ID:            id,
			Force:         true,
			RemoveVolumes: true,
		}); err != nil && !isNoSuchContainer(err) {
			r.log.Warningf("Failed to remove container %s: %#v", id, err)
		}
	}
	r.containerIDs = nil

	return nil
}

// recordContainerID records an ID of a created container
func (r *dockerRunner) recordContainerID(id string) {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	r.containerIDs[id] = time.Now()
}

// unrecordContainerID removes an ID from the list of created containers
func (r *dockerRunner) unrecordContainerID(id string) {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	delete(r.containerIDs, id)
}

// gc performs continues garbage collection of stopped old containers
func (r *dockerRunner) gc() {
	canGC := func(c *docker.Container) bool {
		gcBoundary := time.Now().UTC().Add(-r.gcDelay)
		switch c.State.StateString() {
		case "dead", "exited":
			if c.State.FinishedAt.Before(gcBoundary) {
				// Dead or exited long enough
				return true
			}
		case "created":
			if c.Created.Before(gcBoundary) {
				// Created but not running long enough
				return true
			}
		}
		return false
	}
	for {
		ids := r.gatherCollectableContainerIDs()
		for _, id := range ids {
			c, err := r.client.InspectContainer(id)
			if err != nil {
				if isNoSuchContainer(err) {
					// container no longer exists
					r.unrecordContainerID(id)
				} else {
					r.log.Warningf("Failed to inspect container %s: %#v", id, err)
				}
			} else if canGC(c) {
				// Container is dead for more than 10 minutes, gc it.
				r.log.Infof("Removing old container %s", id)
				if err := r.client.RemoveContainer(docker.RemoveContainerOptions{
					ID:            id,
					RemoveVolumes: true,
				}); err != nil {
					r.log.Warningf("Failed to remove container %s: %#v", id, err)
				} else {
					// Remove succeeded
					r.unrecordContainerID(id)
				}
			}
		}
		time.Sleep(time.Minute)
	}
}

// gatherCollectableContainerIDs returns all container ID's that are old enough to be consider for garbage collection.
func (r *dockerRunner) gatherCollectableContainerIDs() []string {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	var result []string
	gcBoundary := time.Now().Add(-r.gcDelay)
	for id, ts := range r.containerIDs {
		if ts.Before(gcBoundary) {
			result = append(result, id)
		}
	}
	return result
}

// ProcessID returns the pid of the process (if not running in docker)
func (p *dockerContainer) ProcessID() int {
	return 0
}

// ContainerID returns the ID of the docker container that runs the process.
func (p *dockerContainer) ContainerID() string {
	return p.container.ID
}

// ContainerIP returns the IP address of the docker container that runs the process.
func (p *dockerContainer) ContainerIP() string {
	if ns := p.container.NetworkSettings; ns != nil {
		return ns.IPAddress
	}
	return ""
}

// HostPort returns the port on the host that is used to access the given port of the process.
func (p *dockerContainer) HostPort(containerPort int) (int, error) {
	if hostConfig := p.container.HostConfig; hostConfig != nil {
		if hostConfig.NetworkMode == "host" {
			return containerPort, nil
		}
		dockerPort := docker.Port(fmt.Sprintf("%d/tcp", containerPort))
		if binding, ok := hostConfig.PortBindings[dockerPort]; ok && len(binding) > 0 {
			return strconv.Atoi(binding[0].HostPort)
		}
	}
	return 0, fmt.Errorf("Cannot find port mapping.")
}

func (p *dockerContainer) Wait() {
	p.client.WaitContainer(p.container.ID)
}

func (p *dockerContainer) Terminate() error {
	if err := p.client.StopContainer(p.container.ID, stopContainerTimeout); err != nil {
		return maskAny(err)
	}
	return nil
}

func (p *dockerContainer) Kill() error {
	if err := p.client.KillContainer(docker.KillContainerOptions{
		ID: p.container.ID,
	}); err != nil {
		return maskAny(err)
	}
	return nil
}

func (p *dockerContainer) Cleanup() error {
	opts := docker.RemoveContainerOptions{
		ID:            p.container.ID,
		Force:         true,
		RemoveVolumes: true,
	}
	if err := p.client.RemoveContainer(opts); err != nil {
		return maskAny(err)
	}
	return nil
}

// isNoSuchContainer returns true if the given error is (or is caused by) a NoSuchContainer error.
func isNoSuchContainer(err error) bool {
	if _, ok := err.(*docker.NoSuchContainer); ok {
		return true
	}
	if _, ok := errors.Cause(err).(*docker.NoSuchContainer); ok {
		return true
	}
	return false
}

// isNotFound returns true if the given error is (or is caused by) a 404 response error.
func isNotFound(err error) bool {
	if err, ok := errors.Cause(err).(*docker.Error); ok {
		return err.Status == 404
	}
	return false
}
<commit_msg>Comment fix<commit_after>package service

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	docker "github.com/fsouza/go-dockerclient"
	logging "github.com/op/go-logging"
	"github.com/pkg/errors"
)

const (
	stopContainerTimeout = 60 // Seconds before a container is killed (after graceful stop)
	containerFileName    = "CONTAINER"
)

// NewDockerRunner creates a runner that starts processes in a docker container.
func NewDockerRunner(log *logging.Logger, endpoint, image, user, volumesFrom string, gcDelay time.Duration, networkMode string, privileged bool) (Runner, error) {
	client, err := docker.NewClient(endpoint)
	if err != nil {
		return nil, maskAny(err)
	}
	return &dockerRunner{
		log:          log,
		client:       client,
		image:        image,
		user:         user,
		volumesFrom:  volumesFrom,
		containerIDs: make(map[string]time.Time),
		gcDelay:      gcDelay,
		networkMode:  networkMode,
		privileged:   privileged,
	}, nil
}

// dockerRunner implements a Runner that starts processes in a docker container.
type dockerRunner struct {
	log          *logging.Logger
	client       *docker.Client
	image        string
	user         string
	volumesFrom  string
	mutex        sync.Mutex
	containerIDs map[string]time.Time
	gcOnce       sync.Once
	gcDelay      time.Duration
	networkMode  string
	privileged   bool
}

type dockerContainer struct {
	client    *docker.Client
	container *docker.Container
}

func (r *dockerRunner) GetContainerDir(hostDir string) string {
	if r.volumesFrom != "" {
		return hostDir
	}
	return "/data"
}

// GetRunningServer checks if there is already a server process running in the given server directory.
// If that is the case, its process is returned.
// Otherwise nil is returned.
func (r *dockerRunner) GetRunningServer(serverDir string) (Process, error) {
	containerContent, err := ioutil.ReadFile(filepath.Join(serverDir, containerFileName))
	if os.IsNotExist(err) {
		return nil, nil
	} else if err != nil {
		return nil, maskAny(err)
	}
	id := string(containerContent)
	// We found a CONTAINER file, see if this container is still running
	c, err := r.client.InspectContainer(id)
	if err != nil {
		// Container cannot be inspected, assume it no longer exists
		return nil, nil
	}
	// Container can be inspected, check its state
	if !c.State.Running {
		// Container is not running
		return nil, nil
	}
	r.recordContainerID(c.ID)
	// Start gc (once)
	r.startGC()

	// Return container
	return &dockerContainer{
		client:    r.client,
		container: c,
	}, nil
}

func (r *dockerRunner) Start(command string, args []string, volumes []Volume, ports []int, containerName, serverDir string) (Process, error) {
	// Start gc (once)
	r.startGC()

	// Pull docker image
	if err := r.pullImage(r.image); err != nil {
		return nil, maskAny(err)
	}

	// Ensure container name is valid
	containerName = strings.Replace(containerName, ":", "", -1)

	var result Process
	op := func() error {
		// Make sure the container is really gone
		r.log.Debugf("Removing container '%s' (if it exists)", containerName)
		if err := r.client.RemoveContainer(docker.RemoveContainerOptions{
			ID:    containerName,
			Force: true,
		}); err != nil && !isNoSuchContainer(err) {
			r.log.Errorf("Failed to remove container '%s': %v", containerName, err)
		}
		// Try starting it now
		p, err := r.start(command, args, volumes, ports, containerName, serverDir)
		if err != nil {
			return maskAny(err)
		}
		result = p
		return nil
	}

	if err := retry(op, time.Minute*2); err != nil {
		return nil, maskAny(err)
	}
	return result, nil
}

// startGC ensures GC is started (only once)
func (r *dockerRunner) startGC() {
	// Start gc (once)
	r.gcOnce.Do(func() { go r.gc() })
}

// Try to start a command with given arguments
func (r *dockerRunner) start(command string, args []string, volumes []Volume, ports []int, containerName, serverDir string) (Process, error) {
	opts := docker.CreateContainerOptions{
		Name: containerName,
		Config: &docker.Config{
			Image:        r.image,
			Entrypoint:   []string{command},
			Cmd:          args,
			Tty:          true,
			User:         r.user,
			ExposedPorts: make(map[docker.Port]struct{}),
		},
		HostConfig: &docker.HostConfig{
			PortBindings:    make(map[docker.Port][]docker.PortBinding),
			PublishAllPorts: false,
			AutoRemove:      false,
			Privileged:      r.privileged,
		},
	}
	if r.volumesFrom != "" {
		opts.HostConfig.VolumesFrom = []string{r.volumesFrom}
	} else {
		for _, v := range volumes {
			bind := fmt.Sprintf("%s:%s", v.HostPath, v.ContainerPath)
			if v.ReadOnly {
				bind = bind + ":ro"
			}
			opts.HostConfig.Binds = append(opts.HostConfig.Binds, bind)
		}
	}
	if r.networkMode != "" && r.networkMode != "default" {
		opts.HostConfig.NetworkMode = r.networkMode
	} else {
		for _, p := range ports {
			dockerPort := docker.Port(fmt.Sprintf("%d/tcp", p))
			opts.Config.ExposedPorts[dockerPort] = struct{}{}
			opts.HostConfig.PortBindings[dockerPort] = []docker.PortBinding{
				docker.PortBinding{
					HostIP:   "0.0.0.0",
					HostPort: strconv.Itoa(p),
				},
			}
		}
	}
	r.log.Debugf("Creating container %s", containerName)
	c, err := r.client.CreateContainer(opts)
	if err != nil {
		return nil, maskAny(err)
	}
	r.recordContainerID(c.ID) // Record ID so we can clean it up later
	r.log.Debugf("Starting container %s", containerName)
	if err := r.client.StartContainer(c.ID, opts.HostConfig); err != nil {
		return nil, maskAny(err)
	}
	r.log.Debugf("Started container %s", containerName)
	// Write container ID to disk
	containerFilePath := filepath.Join(serverDir, containerFileName)
	if err := ioutil.WriteFile(containerFilePath, []byte(c.ID), 0755); err != nil {
		r.log.Errorf("Failed to store container ID in '%s': %v", containerFilePath, err)
	}
	// Inspect container to make sure we have the latest info
	c, err = r.client.InspectContainer(c.ID)
	if err != nil {
		return nil, maskAny(err)
	}
	return &dockerContainer{
		client:    r.client,
		container: c,
	}, nil
}

// pullImage tries to pull the given image.
// It retries several times upon failure.
func (r *dockerRunner) pullImage(image string) error {
	// Pull docker image
	repo, tag := docker.ParseRepositoryTag(r.image)

	op := func() error {
		r.log.Debugf("Pulling image %s:%s", repo, tag)
		if err := r.client.PullImage(docker.PullImageOptions{
			Repository: repo,
			Tag:        tag,
		}, docker.AuthConfiguration{}); err != nil {
			if isNotFound(err) {
				return maskAny(&PermanentError{err})
			}
			return maskAny(err)
		}
		return nil
	}

	if err := retry(op, time.Minute*2); err != nil {
		return maskAny(err)
	}
	return nil
}

func (r *dockerRunner) CreateStartArangodbCommand(index int, masterIP string, masterPort string) string {
	addr := masterIP
	hostPort := 4000 + (portOffsetIncrement * (index - 1))
	if masterPort != "" {
		addr = net.JoinHostPort(addr, masterPort)
		masterPortI, _ := strconv.Atoi(masterPort)
		hostPort = masterPortI + (portOffsetIncrement * (index - 1))
	}
	var netArgs string
	if r.networkMode == "" || r.networkMode == "default" {
		netArgs = fmt.Sprintf("-p %d:4000", hostPort)
	} else {
		netArgs = fmt.Sprintf("--net=%s", r.networkMode)
	}
	lines := []string{
		fmt.Sprintf("docker volume create arangodb%d &&", index),
		fmt.Sprintf("docker run -it --name=adb%d --rm %s -v arangodb%d:/data", index, netArgs, index),
		fmt.Sprintf("-v /var/run/docker.sock:/var/run/docker.sock arangodb/arangodb-starter"),
		fmt.Sprintf("--dockerContainer=adb%d --ownAddress=%s --join=%s", index, masterIP, addr),
	}
	return strings.Join(lines, " \\\n    ")
}

// Cleanup after all processes are dead and have been cleaned themselves
func (r *dockerRunner) Cleanup() error {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	for id := range r.containerIDs {
		r.log.Infof("Removing container %s", id)
		if err := r.client.RemoveContainer(docker.RemoveContainerOptions{
			ID:            id,
			Force:         true,
			RemoveVolumes: true,
		}); err != nil && !isNoSuchContainer(err) {
			r.log.Warningf("Failed to remove container %s: %#v", id, err)
		}
	}
	r.containerIDs = nil

	return nil
}

// recordContainerID records an ID of a created container
func (r *dockerRunner) recordContainerID(id string) {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	r.containerIDs[id] = time.Now()
}

// unrecordContainerID removes an ID from the list of created containers
func (r *dockerRunner) unrecordContainerID(id string) {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	delete(r.containerIDs, id)
}

// gc performs continues garbage collection of stopped old containers
func (r *dockerRunner) gc() {
	canGC := func(c *docker.Container) bool {
		gcBoundary := time.Now().UTC().Add(-r.gcDelay)
		switch c.State.StateString() {
		case "dead", "exited":
			if c.State.FinishedAt.Before(gcBoundary) {
				// Dead or exited long enough
				return true
			}
		case "created":
			if c.Created.Before(gcBoundary) {
				// Created but not running long enough
				return true
			}
		}
		return false
	}
	for {
		ids := r.gatherCollectableContainerIDs()
		for _, id := range ids {
			c, err := r.client.InspectContainer(id)
			if err != nil {
				if isNoSuchContainer(err) {
					// container no longer exists
					r.unrecordContainerID(id)
				} else {
					r.log.Warningf("Failed to inspect container %s: %#v", id, err)
				}
			} else if canGC(c) {
				// Container is dead for more than 10 minutes, gc it.
				r.log.Infof("Removing old container %s", id)
				if err := r.client.RemoveContainer(docker.RemoveContainerOptions{
					ID:            id,
					RemoveVolumes: true,
				}); err != nil {
					r.log.Warningf("Failed to remove container %s: %#v", id, err)
				} else {
					// Remove succeeded
					r.unrecordContainerID(id)
				}
			}
		}
		time.Sleep(time.Minute)
	}
}

// gatherCollectableContainerIDs returns all container ID's that are old enough to be consider for garbage collection.
func (r *dockerRunner) gatherCollectableContainerIDs() []string {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	var result []string
	gcBoundary := time.Now().Add(-r.gcDelay)
	for id, ts := range r.containerIDs {
		if ts.Before(gcBoundary) {
			result = append(result, id)
		}
	}
	return result
}

// ProcessID returns the pid of the process (if not running in docker)
func (p *dockerContainer) ProcessID() int {
	return 0
}

// ContainerID returns the ID of the docker container that runs the process.
func (p *dockerContainer) ContainerID() string {
	return p.container.ID
}

// ContainerIP returns the IP address of the docker container that runs the process.
func (p *dockerContainer) ContainerIP() string {
	if ns := p.container.NetworkSettings; ns != nil {
		return ns.IPAddress
	}
	return ""
}

// HostPort returns the port on the host that is used to access the given port of the process.
func (p *dockerContainer) HostPort(containerPort int) (int, error) {
	if hostConfig := p.container.HostConfig; hostConfig != nil {
		if hostConfig.NetworkMode == "host" {
			return containerPort, nil
		}
		dockerPort := docker.Port(fmt.Sprintf("%d/tcp", containerPort))
		if binding, ok := hostConfig.PortBindings[dockerPort]; ok && len(binding) > 0 {
			return strconv.Atoi(binding[0].HostPort)
		}
	}
	return 0, fmt.Errorf("Cannot find port mapping.")
}

func (p *dockerContainer) Wait() {
	p.client.WaitContainer(p.container.ID)
}

func (p *dockerContainer) Terminate() error {
	if err := p.client.StopContainer(p.container.ID, stopContainerTimeout); err != nil {
		return maskAny(err)
	}
	return nil
}

func (p *dockerContainer) Kill() error {
	if err := p.client.KillContainer(docker.KillContainerOptions{
		ID: p.container.ID,
	}); err != nil {
		return maskAny(err)
	}
	return nil
}

func (p *dockerContainer) Cleanup() error {
	opts := docker.RemoveContainerOptions{
		ID:            p.container.ID,
		Force:         true,
		RemoveVolumes: true,
	}
	if err := p.client.RemoveContainer(opts); err != nil {
		return maskAny(err)
	}
	return nil
}

// isNoSuchContainer returns true if the given error is (or is caused by) a NoSuchContainer error.
func isNoSuchContainer(err error) bool {
	if _, ok := err.(*docker.NoSuchContainer); ok {
		return true
	}
	if _, ok := errors.Cause(err).(*docker.NoSuchContainer); ok {
		return true
	}
	return false
}

// isNotFound returns true if the given error is (or is caused by) a 404 response error.
func isNotFound(err error) bool {
	if err, ok := errors.Cause(err).(*docker.Error); ok {
		return err.Status == 404
	}
	return false
}
<|endoftext|>


<commit_before>package endly_test

import (
	_ "github.com/mattn/go-sqlite3"
	"github.com/viant/endly"
	"github.com/viant/toolbox"

	"github.com/viant/toolbox/url"
	"errors"
	"testing"
	"github.com/stretchr/testify/assert"
	"os/exec"
	"path"
	"strings"
)

func getServiceWithWorkflow(workflowURI string) (endly.Manager, endly.Service, error) {
	manager := endly.NewManager()
	service, err := manager.Service(endly.WorkflowServiceID)
	if err == nil {

		context := manager.NewContext(toolbox.NewContext())
		response := service.Run(context, &endly.WorkflowLoadRequest{
			Source: url.NewResource(workflowURI),
		})
		if response.Error != "" {
			return nil, nil, errors.New(response.Error)
		}
	}
	return manager, service, err
}

func TestWorkflowService_RunDsUnitWorkflow(t *testing.T) {

	exec.Command("rm", "-rf", "/tmp/endly/test/workflow/dsunit").CombinedOutput()
	toolbox.CreateDirIfNotExist("/tmp/endly/test/workflow/dsunit")
	manager, service, err := getServiceWithWorkflow("test/workflow/dsunit/workflow.csv")
	if assert.Nil(t, err) {

		context := manager.NewContext(toolbox.NewContext())
		serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
			Name:  "workflow",
			Tasks: "prepare",
			Params: map[string]interface{}{
				"param1": 1,
			},
		})
		assert.Equal(t, "", serviceResponse.Error)
		response, ok := serviceResponse.Response.(*endly.WorkflowRunResponse)

		if assert.True(t, ok) {
			assert.NotNil(t, response)
			var dsunit = toolbox.AsMap(response.Data["dsunit"])
			var records = toolbox.AsSlice(dsunit["USER_ACCOUNT"])
			assert.EqualValues(t, 3, len(records))

		}

		context = manager.NewContext(toolbox.NewContext())
		serviceResponse = service.Run(context, &endly.WorkflowRunRequest{
			Name:  "workflow",
			Tasks: "*",
			Params: map[string]interface{}{
				"param1": 1,
			},
			EnableLogging:    true,
			LoggingDirectory: "/tmp/dsunit/",
		})
		assert.Equal(t, "", serviceResponse.Error)

		response, ok = serviceResponse.Response.(*endly.WorkflowRunResponse)
		assert.NotNil(t, response)
		var dsunit = toolbox.AsMap(response.Data["dsunit"])
		var records = toolbox.AsSlice(dsunit["USER_ACCOUNT"])
		assert.EqualValues(t, 0, len(records)) //validate task shift elements from USER_ACCCOUNT array.

	}
}

func TestWorkflowService_RunHttpWorkflow(t *testing.T) {

	baseDir := toolbox.CallerDirectory(3)
	err := endly.StartHttpServer(8113, &endly.HttpServerTrips{
		IndexKeys:     []string{endly.MethodKey, endly.URLKey, endly.BodyKey, endly.CookieKey, endly.ContentTypeKey},
		BaseDirectory: path.Join(baseDir, "test/http/runner/http_workflow"),
	})

	if ! assert.Nil(t, err) {
		return
	}

	manager, service, err := getServiceWithWorkflow("test/workflow/http/workflow.csv")
	if assert.Nil(t, err) {

		context := manager.NewContext(toolbox.NewContext())
		serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
			Name:  "http_workflow",
			Tasks: "*",
			Params: map[string]interface{}{
				"appServer": "http://127.0.0.1:8113",
			},
			PublishParameters: true,
			EnableLogging:    true,
			LoggingDirectory: "/tmp/http/",
		})
		assert.EqualValues(t, "", serviceResponse.Error)
		response, ok := serviceResponse.Response.(*endly.WorkflowRunResponse)
		if assert.True(t, ok) {

			httpResponses := toolbox.AsSlice(response.Data["httpResponses"])
			assert.EqualValues(t, 3,len(httpResponses))
			for _, item := range httpResponses {
				httpResponse := toolbox.AsMap(item)
				assert.EqualValues(t, 200, httpResponse["Code"])
			}
		}
	}
}

func TestWorkflowService_RunLifeCycle(t *testing.T) {

	manager, service, err := getServiceWithWorkflow("test/workflow/lifecycle/workflow.csv")
	if assert.Nil(t, err) {

		context := manager.NewContext(toolbox.NewContext())
		serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
			Name:  "lifecycle",
			Tasks: "*",
			Params: map[string]interface{}{
				"object": map[string]interface{}{
					"key1":1,
					"key2":"abc",
				},
			},
			PublishParameters: true,
			EnableLogging:    true,
			LoggingDirectory: "/tmp/lifecycle/",
		})

		if assert.EqualValues(t, "", serviceResponse.Error) {
			response, ok := serviceResponse.Response.(*endly.WorkflowRunResponse)
			if assert.True(t, ok) {
				assert.EqualValues(t, 2, response.Data["testPassed"])
				var anArray  =toolbox.AsSlice(response.Data["array"])
				assert.EqualValues(t, 2, anArray[0])
				assert.EqualValues(t, 3, response.Data["counter"])
				var anObject= toolbox.AsMap(response.Data["object"])
				assert.EqualValues(t, 1, anObject["key1"])
				assert.EqualValues(t, "200", anObject["shift"])
			}
		}
	}
}

func TestWorkflowService_RunBroken(t *testing.T) {

	{
		//request empty error

		manager, service, err := getServiceWithWorkflow("test/workflow/broken/broken1.csv")
		if assert.Nil(t, err) {
			context := manager.NewContext(toolbox.NewContext())
			serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
				Name:              "broken1",
				Tasks:             "*",
				Params:            map[string]interface{}{},
				PublishParameters: true,
			})
			assert.EqualValues(t, true, strings.Contains(serviceResponse.Error, "failed to evaluate request"), serviceResponse.Error)
		}
	}
	{
		//unsupported action error

		manager, service, err := getServiceWithWorkflow("test/workflow/broken/broken2.csv")
		if assert.Nil(t, err) {
			context := manager.NewContext(toolbox.NewContext())
			serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
				Name:              "broken2",
				Tasks:             "*",
				Params:            map[string]interface{}{},
				PublishParameters: true,
			})
			assert.EqualValues(t, true, strings.Contains(serviceResponse.Error, "unsupported action: aaa"), serviceResponse.Error)
		}
	}

	{
		//unsupported action error

		manager, service, err := getServiceWithWorkflow("test/workflow/broken/broken2.csv")
		if assert.Nil(t, err) {
			context := manager.NewContext(toolbox.NewContext())
			serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
				Name:              "broken2",
				Tasks:             "*",
				Params:            map[string]interface{}{},
				PublishParameters: true,
			})
			assert.EqualValues(t, true, strings.Contains(serviceResponse.Error, "unsupported action: aaa"), serviceResponse.Error)
		}
	}

	{
		//unsupported service error

		manager, service, err := getServiceWithWorkflow("test/workflow/broken/broken3.csv")
		if assert.Nil(t, err) {
			context := manager.NewContext(toolbox.NewContext())
			serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
				Name:              "broken3",
				Tasks:             "*",
				Params:            map[string]interface{}{},
				PublishParameters: true,
			})
			assert.EqualValues(t, true, strings.Contains(serviceResponse.Error, "failed to lookup service: 'aaa'"), serviceResponse.Error)
		}
	}

	{
		//calling invalid workflow

		manager, service, err := getServiceWithWorkflow("test/workflow/broken/broken4.csv")
		if assert.Nil(t, err) {
			context := manager.NewContext(toolbox.NewContext())
			serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
				Name:              "broken4",
				Tasks:             "*",
				Params:            map[string]interface{}{},
				PublishParameters: true,
			})
			assert.EqualValues(t, true, strings.Contains(serviceResponse.Error, "failed to lookup service: 'aaa'"), serviceResponse.Error)
		}
	}

}
//TODO patch async task reporting<commit_msg>updated unit test, patched minor issues<commit_after>package endly_test

import (
	_ "github.com/mattn/go-sqlite3"
	"github.com/viant/endly"
	"github.com/viant/toolbox"

	"github.com/viant/toolbox/url"
	"errors"
	"testing"
	"github.com/stretchr/testify/assert"
	"os/exec"
	"path"
	"strings"
)

func getServiceWithWorkflow(workflowURI string) (endly.Manager, endly.Service, error) {
	manager := endly.NewManager()
	service, err := manager.Service(endly.WorkflowServiceID)
	if err == nil {

		context := manager.NewContext(toolbox.NewContext())
		response := service.Run(context, &endly.WorkflowLoadRequest{
			Source: url.NewResource(workflowURI),
		})
		if response.Error != "" {
			return nil, nil, errors.New(response.Error)
		}
	}
	return manager, service, err
}

func TestWorkflowService_RunDsUnitWorkflow(t *testing.T) {

	exec.Command("rm", "-rf", "/tmp/endly/test/workflow/dsunit").CombinedOutput()
	toolbox.CreateDirIfNotExist("/tmp/endly/test/workflow/dsunit")
	manager, service, err := getServiceWithWorkflow("test/workflow/dsunit/workflow.csv")
	if assert.Nil(t, err) {

		context := manager.NewContext(toolbox.NewContext())
		serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
			Name:  "workflow",
			Tasks: "prepare",
			Params: map[string]interface{}{
				"param1": 1,
			},
		})
		assert.Equal(t, "", serviceResponse.Error)
		response, ok := serviceResponse.Response.(*endly.WorkflowRunResponse)

		if assert.True(t, ok) {
			assert.NotNil(t, response)
			var dsunit = toolbox.AsMap(response.Data["dsunit"])
			var records = toolbox.AsSlice(dsunit["USER_ACCOUNT"])
			assert.EqualValues(t, 3, len(records))

		}

		context = manager.NewContext(toolbox.NewContext())
		serviceResponse = service.Run(context, &endly.WorkflowRunRequest{
			Name:  "workflow",
			Tasks: "*",
			Params: map[string]interface{}{
				"param1": 1,
			},
			EnableLogging:    true,
			LoggingDirectory: "/tmp/dsunit/",
		})
		assert.Equal(t, "", serviceResponse.Error)

		response, ok = serviceResponse.Response.(*endly.WorkflowRunResponse)
		assert.NotNil(t, response)
		var dsunit = toolbox.AsMap(response.Data["dsunit"])
		var records = toolbox.AsSlice(dsunit["USER_ACCOUNT"])
		assert.EqualValues(t, 0, len(records)) //validate task shift elements from USER_ACCCOUNT array.

	}
}

func TestWorkflowService_RunHttpWorkflow(t *testing.T) {

	baseDir := toolbox.CallerDirectory(3)
	err := endly.StartHttpServer(8113, &endly.HttpServerTrips{
		IndexKeys:     []string{endly.MethodKey, endly.URLKey, endly.BodyKey, endly.CookieKey, endly.ContentTypeKey},
		BaseDirectory: path.Join(baseDir, "test/http/runner/http_workflow"),
	})

	if ! assert.Nil(t, err) {
		return
	}

	manager, service, err := getServiceWithWorkflow("test/workflow/http/workflow.csv")
	if assert.Nil(t, err) {

		context := manager.NewContext(toolbox.NewContext())
		serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
			Name:  "http_workflow",
			Tasks: "*",
			Params: map[string]interface{}{
				"appServer": "http://127.0.0.1:8113",
			},
			PublishParameters: true,
			EnableLogging:    true,
			LoggingDirectory: "/tmp/http/",
		})
		assert.EqualValues(t, "", serviceResponse.Error)
		response, ok := serviceResponse.Response.(*endly.WorkflowRunResponse)
		if assert.True(t, ok) {

			httpResponses := toolbox.AsSlice(response.Data["httpResponses"])
			assert.EqualValues(t, 3,len(httpResponses))
			for _, item := range httpResponses {
				httpResponse := toolbox.AsMap(item)
				assert.EqualValues(t, 200, httpResponse["Code"])
			}
		}
	}
}

func TestWorkflowService_RunLifeCycle(t *testing.T) {

	manager, service, err := getServiceWithWorkflow("test/workflow/lifecycle/workflow.csv")
	if assert.Nil(t, err) {

		context := manager.NewContext(toolbox.NewContext())
		serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
			Name:  "lifecycle",
			Tasks: "*",
			Params: map[string]interface{}{
				"object": map[string]interface{}{
					"key1":1,
					"key2":"abc",
				},
			},
			PublishParameters: true,
			EnableLogging:    true,
			LoggingDirectory: "/tmp/lifecycle/",
		})

		if assert.EqualValues(t, "", serviceResponse.Error) {
			response, ok := serviceResponse.Response.(*endly.WorkflowRunResponse)
			if assert.True(t, ok) {
				assert.EqualValues(t, 2, response.Data["testPassed"])
				var anArray  =toolbox.AsSlice(response.Data["array"])
				assert.EqualValues(t, 2, anArray[0])
				assert.EqualValues(t, 3, response.Data["counter"])
				var anObject= toolbox.AsMap(response.Data["object"])
				assert.EqualValues(t, 1, anObject["key1"])
				assert.EqualValues(t, "200", anObject["shift"])
			}
		}
	}
}

func TestWorkflowService_RunBroken(t *testing.T) {

	{
		//request empty error

		manager, service, err := getServiceWithWorkflow("test/workflow/broken/broken1.csv")
		if assert.Nil(t, err) {
			context := manager.NewContext(toolbox.NewContext())
			serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
				Name:              "broken1",
				Tasks:             "*",
				Params:            map[string]interface{}{},
				PublishParameters: true,
			})
			assert.EqualValues(t, true, strings.Contains(serviceResponse.Error, "failed to evaluate request"), serviceResponse.Error)
		}
	}
	{
		//unsupported action error

		manager, service, err := getServiceWithWorkflow("test/workflow/broken/broken2.csv")
		if assert.Nil(t, err) {
			context := manager.NewContext(toolbox.NewContext())
			serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
				Name:              "broken2",
				Tasks:             "*",
				Params:            map[string]interface{}{},
				PublishParameters: true,
			})
			assert.EqualValues(t, true, strings.Contains(serviceResponse.Error, "unsupported action: aaa"), serviceResponse.Error)
		}
	}

	{
		//unsupported action error

		manager, service, err := getServiceWithWorkflow("test/workflow/broken/broken2.csv")
		if assert.Nil(t, err) {
			context := manager.NewContext(toolbox.NewContext())
			serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
				Name:              "broken2",
				Tasks:             "*",
				Params:            map[string]interface{}{},
				PublishParameters: true,
			})
			assert.EqualValues(t, true, strings.Contains(serviceResponse.Error, "unsupported action: aaa"), serviceResponse.Error)
		}
	}

	{
		//unsupported service error

		manager, service, err := getServiceWithWorkflow("test/workflow/broken/broken3.csv")
		if assert.Nil(t, err) {
			context := manager.NewContext(toolbox.NewContext())
			serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
				Name:              "broken3",
				Tasks:             "*",
				Params:            map[string]interface{}{},
				PublishParameters: true,
			})
			assert.EqualValues(t, true, strings.Contains(serviceResponse.Error, "failed to lookup service: 'aaa'"), serviceResponse.Error)
		}
	}

	{
		//calling invalid workflow

		manager, service, err := getServiceWithWorkflow("test/workflow/broken/broken4.csv")
		if assert.Nil(t, err) {
			context := manager.NewContext(toolbox.NewContext())
			serviceResponse := service.Run(context, &endly.WorkflowRunRequest{
				Name:              "broken4",
				Tasks:             "*",
				Params:            map[string]interface{}{},
				PublishParameters: true,
			})
			assert.EqualValues(t, true, strings.Contains(serviceResponse.Error, "failed to load workflow"), serviceResponse.Error)
		}
	}

}
//TODO patch async task reporting<|endoftext|>


<commit_before>package otto

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strings"
)

type _builtinJSON_parseContext struct {
	call    FunctionCall
	reviver Value
}

func builtinJSON_parse(call FunctionCall) Value {
	ctx := _builtinJSON_parseContext{
		call: call,
	}
	revive := false
	if reviver := call.Argument(1); reviver.isCallable() {
		revive = true
		ctx.reviver = reviver
	}

	var root interface{}
	err := json.Unmarshal([]byte(call.Argument(0).string()), &root)
	if err != nil {
		panic(call.runtime.panicSyntaxError(err.Error()))
	}
	value, exists := builtinJSON_parseWalk(ctx, root)
	if !exists {
		value = Value{}
	}
	if revive {
		root := ctx.call.runtime.newObject()
		root.put("", value, false)
		return builtinJSON_reviveWalk(ctx, root, "")
	}
	return value
}

func builtinJSON_reviveWalk(ctx _builtinJSON_parseContext, holder *_object, name string) Value {
	value := holder.get(name)
	if object := value._object(); object != nil {
		if isArray(object) {
			length := int64(objectLength(object))
			for index := int64(0); index < length; index += 1 {
				name := arrayIndexToString(index)
				value := builtinJSON_reviveWalk(ctx, object, name)
				if value.IsUndefined() {
					object.delete(name, false)
				} else {
					object.defineProperty(name, value, 0111, false)
				}
			}
		} else {
			object.enumerate(false, func(name string) bool {
				value := builtinJSON_reviveWalk(ctx, object, name)
				if value.IsUndefined() {
					object.delete(name, false)
				} else {
					object.defineProperty(name, value, 0111, false)
				}
				return true
			})
		}
	}
	return ctx.reviver.call(ctx.call.runtime, toValue_object(holder), name, value)
}

func builtinJSON_parseWalk(ctx _builtinJSON_parseContext, rawValue interface{}) (Value, bool) {
	switch value := rawValue.(type) {
	case nil:
		return nullValue, true
	case bool:
		return toValue_bool(value), true
	case string:
		return toValue_string(value), true
	case float64:
		return toValue_float64(value), true
	case []interface{}:
		arrayValue := make([]Value, len(value))
		for index, rawValue := range value {
			if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists {
				arrayValue[index] = value
			}
		}
		return toValue_object(ctx.call.runtime.newArrayOf(arrayValue)), true
	case map[string]interface{}:
		object := ctx.call.runtime.newObject()
		for name, rawValue := range value {
			if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists {
				object.put(name, value, false)
			}
		}
		return toValue_object(object), true
	}
	return Value{}, false
}

type _builtinJSON_stringifyContext struct {
	call             FunctionCall
	stack            []*_object
	propertyList     []string
	replacerFunction *Value
	gap              string
}

func builtinJSON_stringify(call FunctionCall) Value {
	ctx := _builtinJSON_stringifyContext{
		call:  call,
		stack: []*_object{nil},
	}
	replacer := call.Argument(1)._object()
	if replacer != nil {
		if isArray(replacer) {
			length := objectLength(replacer)
			seen := map[string]bool{}
			propertyList := make([]string, length)
			length = 0
			for index, _ := range propertyList {
				value := replacer.get(arrayIndexToString(int64(index)))
				switch value.kind {
				case valueObject:
					switch value.value.(*_object).class {
					case classString:
					case classNumber:
					default:
						continue
					}
				case valueString:
				case valueNumber:
				default:
					continue
				}
				name := value.string()
				if seen[name] {
					continue
				}
				seen[name] = true
				length += 1
				propertyList[index] = name
			}
			ctx.propertyList = propertyList[0:length]
		} else if replacer.class == classFunction {
			value := toValue_object(replacer)
			ctx.replacerFunction = &value
		}
	}
	if spaceValue, exists := call.getArgument(2); exists {
		if spaceValue.kind == valueObject {
			switch spaceValue.value.(*_object).class {
			case classString:
				spaceValue = toValue_string(spaceValue.string())
			case classNumber:
				spaceValue = spaceValue.numberValue()
			}
		}
		switch spaceValue.kind {
		case valueString:
			value := spaceValue.string()
			if len(value) > 10 {
				ctx.gap = value[0:10]
			} else {
				ctx.gap = value
			}
		case valueNumber:
			value := spaceValue.number().int64
			if value > 10 {
				value = 10
			} else if value < 0 {
				value = 0
			}
			ctx.gap = strings.Repeat(" ", int(value))
		}
	}
	holder := call.runtime.newObject()
	holder.put("", call.Argument(0), false)
	value, exists := builtinJSON_stringifyWalk(ctx, "", holder)
	if !exists {
		return Value{}
	}
	valueJSON, err := json.Marshal(value)
	if err != nil {
		panic(call.runtime.panicTypeError(err.Error()))
	}
	if ctx.gap != "" {
		valueJSON1 := bytes.Buffer{}
		json.Indent(&valueJSON1, valueJSON, "", ctx.gap)
		valueJSON = valueJSON1.Bytes()
	}
	return toValue_string(string(valueJSON))
}

func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, holder *_object) (interface{}, bool) {
	value := holder.get(key)

	if value.IsObject() {
		object := value._object()
		if toJSON := object.get("toJSON"); toJSON.IsFunction() {
			value = toJSON.call(ctx.call.runtime, value, key)
		} else {
			// If the object is a GoStruct or something that implements json.Marshaler
			if object.objectClass.marshalJSON != nil {
				marshaler := object.objectClass.marshalJSON(object)
				if marshaler != nil {
					return marshaler, true
				}
			}
		}
	}

	if ctx.replacerFunction != nil {
		value = (*ctx.replacerFunction).call(ctx.call.runtime, toValue_object(holder), key, value)
	}

	if value.kind == valueObject {
		switch value.value.(*_object).class {
		case classBoolean:
			value = value._object().value.(Value)
		case classString:
			value = toValue_string(value.string())
		case classNumber:
			value = value.numberValue()
		}
	}

	switch value.kind {
	case valueBoolean:
		return value.bool(), true
	case valueString:
		return value.string(), true
	case valueNumber:
		integer := value.number()
		switch integer.kind {
		case numberInteger:
			return integer.int64, true
		case numberFloat:
			return integer.float64, true
		default:
			return nil, true
		}
	case valueNull:
		return nil, true
	case valueObject:
		holder := value._object()
		if value := value._object(); nil != value {
			for _, object := range ctx.stack {
				if holder == object {
					panic(ctx.call.runtime.panicTypeError("Converting circular structure to JSON"))
				}
			}
			ctx.stack = append(ctx.stack, value)
			defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }()
		}
		if isArray(holder) {
			var length uint32
			switch value := holder.get(propertyLength).value.(type) {
			case uint32:
				length = value
			case int:
				if value >= 0 {
					length = uint32(value)
				}
			default:
				panic(ctx.call.runtime.panicTypeError(fmt.Sprintf("JSON.stringify: invalid length: %v (%[1]T)", value)))
			}
			array := make([]interface{}, length)
			for index, _ := range array {
				name := arrayIndexToString(int64(index))
				value, _ := builtinJSON_stringifyWalk(ctx, name, holder)
				array[index] = value
			}
			return array, true
		} else if holder.class != classFunction {
			object := map[string]interface{}{}
			if ctx.propertyList != nil {
				for _, name := range ctx.propertyList {
					value, exists := builtinJSON_stringifyWalk(ctx, name, holder)
					if exists {
						object[name] = value
					}
				}
			} else {
				// Go maps are without order, so this doesn't conform to the ECMA ordering
				// standard, but oh well...
				holder.enumerate(false, func(name string) bool {
					value, exists := builtinJSON_stringifyWalk(ctx, name, holder)
					if exists {
						object[name] = value
					}
					return true
				})
			}
			return object, true
		}
	}
	return nil, false
}
<commit_msg>fix: remove unnecessary dereference (#321)<commit_after>package otto

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strings"
)

type _builtinJSON_parseContext struct {
	call    FunctionCall
	reviver Value
}

func builtinJSON_parse(call FunctionCall) Value {
	ctx := _builtinJSON_parseContext{
		call: call,
	}
	revive := false
	if reviver := call.Argument(1); reviver.isCallable() {
		revive = true
		ctx.reviver = reviver
	}

	var root interface{}
	err := json.Unmarshal([]byte(call.Argument(0).string()), &root)
	if err != nil {
		panic(call.runtime.panicSyntaxError(err.Error()))
	}
	value, exists := builtinJSON_parseWalk(ctx, root)
	if !exists {
		value = Value{}
	}
	if revive {
		root := ctx.call.runtime.newObject()
		root.put("", value, false)
		return builtinJSON_reviveWalk(ctx, root, "")
	}
	return value
}

func builtinJSON_reviveWalk(ctx _builtinJSON_parseContext, holder *_object, name string) Value {
	value := holder.get(name)
	if object := value._object(); object != nil {
		if isArray(object) {
			length := int64(objectLength(object))
			for index := int64(0); index < length; index += 1 {
				name := arrayIndexToString(index)
				value := builtinJSON_reviveWalk(ctx, object, name)
				if value.IsUndefined() {
					object.delete(name, false)
				} else {
					object.defineProperty(name, value, 0111, false)
				}
			}
		} else {
			object.enumerate(false, func(name string) bool {
				value := builtinJSON_reviveWalk(ctx, object, name)
				if value.IsUndefined() {
					object.delete(name, false)
				} else {
					object.defineProperty(name, value, 0111, false)
				}
				return true
			})
		}
	}
	return ctx.reviver.call(ctx.call.runtime, toValue_object(holder), name, value)
}

func builtinJSON_parseWalk(ctx _builtinJSON_parseContext, rawValue interface{}) (Value, bool) {
	switch value := rawValue.(type) {
	case nil:
		return nullValue, true
	case bool:
		return toValue_bool(value), true
	case string:
		return toValue_string(value), true
	case float64:
		return toValue_float64(value), true
	case []interface{}:
		arrayValue := make([]Value, len(value))
		for index, rawValue := range value {
			if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists {
				arrayValue[index] = value
			}
		}
		return toValue_object(ctx.call.runtime.newArrayOf(arrayValue)), true
	case map[string]interface{}:
		object := ctx.call.runtime.newObject()
		for name, rawValue := range value {
			if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists {
				object.put(name, value, false)
			}
		}
		return toValue_object(object), true
	}
	return Value{}, false
}

type _builtinJSON_stringifyContext struct {
	call             FunctionCall
	stack            []*_object
	propertyList     []string
	replacerFunction *Value
	gap              string
}

func builtinJSON_stringify(call FunctionCall) Value {
	ctx := _builtinJSON_stringifyContext{
		call:  call,
		stack: []*_object{nil},
	}
	replacer := call.Argument(1)._object()
	if replacer != nil {
		if isArray(replacer) {
			length := objectLength(replacer)
			seen := map[string]bool{}
			propertyList := make([]string, length)
			length = 0
			for index, _ := range propertyList {
				value := replacer.get(arrayIndexToString(int64(index)))
				switch value.kind {
				case valueObject:
					switch value.value.(*_object).class {
					case classString:
					case classNumber:
					default:
						continue
					}
				case valueString:
				case valueNumber:
				default:
					continue
				}
				name := value.string()
				if seen[name] {
					continue
				}
				seen[name] = true
				length += 1
				propertyList[index] = name
			}
			ctx.propertyList = propertyList[0:length]
		} else if replacer.class == classFunction {
			value := toValue_object(replacer)
			ctx.replacerFunction = &value
		}
	}
	if spaceValue, exists := call.getArgument(2); exists {
		if spaceValue.kind == valueObject {
			switch spaceValue.value.(*_object).class {
			case classString:
				spaceValue = toValue_string(spaceValue.string())
			case classNumber:
				spaceValue = spaceValue.numberValue()
			}
		}
		switch spaceValue.kind {
		case valueString:
			value := spaceValue.string()
			if len(value) > 10 {
				ctx.gap = value[0:10]
			} else {
				ctx.gap = value
			}
		case valueNumber:
			value := spaceValue.number().int64
			if value > 10 {
				value = 10
			} else if value < 0 {
				value = 0
			}
			ctx.gap = strings.Repeat(" ", int(value))
		}
	}
	holder := call.runtime.newObject()
	holder.put("", call.Argument(0), false)
	value, exists := builtinJSON_stringifyWalk(ctx, "", holder)
	if !exists {
		return Value{}
	}
	valueJSON, err := json.Marshal(value)
	if err != nil {
		panic(call.runtime.panicTypeError(err.Error()))
	}
	if ctx.gap != "" {
		valueJSON1 := bytes.Buffer{}
		json.Indent(&valueJSON1, valueJSON, "", ctx.gap)
		valueJSON = valueJSON1.Bytes()
	}
	return toValue_string(string(valueJSON))
}

func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, holder *_object) (interface{}, bool) {
	value := holder.get(key)

	if value.IsObject() {
		object := value._object()
		if toJSON := object.get("toJSON"); toJSON.IsFunction() {
			value = toJSON.call(ctx.call.runtime, value, key)
		} else {
			// If the object is a GoStruct or something that implements json.Marshaler
			if object.objectClass.marshalJSON != nil {
				marshaler := object.objectClass.marshalJSON(object)
				if marshaler != nil {
					return marshaler, true
				}
			}
		}
	}

	if ctx.replacerFunction != nil {
		value = ctx.replacerFunction.call(ctx.call.runtime, toValue_object(holder), key, value)
	}

	if value.kind == valueObject {
		switch value.value.(*_object).class {
		case classBoolean:
			value = value._object().value.(Value)
		case classString:
			value = toValue_string(value.string())
		case classNumber:
			value = value.numberValue()
		}
	}

	switch value.kind {
	case valueBoolean:
		return value.bool(), true
	case valueString:
		return value.string(), true
	case valueNumber:
		integer := value.number()
		switch integer.kind {
		case numberInteger:
			return integer.int64, true
		case numberFloat:
			return integer.float64, true
		default:
			return nil, true
		}
	case valueNull:
		return nil, true
	case valueObject:
		holder := value._object()
		if value := value._object(); nil != value {
			for _, object := range ctx.stack {
				if holder == object {
					panic(ctx.call.runtime.panicTypeError("Converting circular structure to JSON"))
				}
			}
			ctx.stack = append(ctx.stack, value)
			defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }()
		}
		if isArray(holder) {
			var length uint32
			switch value := holder.get(propertyLength).value.(type) {
			case uint32:
				length = value
			case int:
				if value >= 0 {
					length = uint32(value)
				}
			default:
				panic(ctx.call.runtime.panicTypeError(fmt.Sprintf("JSON.stringify: invalid length: %v (%[1]T)", value)))
			}
			array := make([]interface{}, length)
			for index, _ := range array {
				name := arrayIndexToString(int64(index))
				value, _ := builtinJSON_stringifyWalk(ctx, name, holder)
				array[index] = value
			}
			return array, true
		} else if holder.class != classFunction {
			object := map[string]interface{}{}
			if ctx.propertyList != nil {
				for _, name := range ctx.propertyList {
					value, exists := builtinJSON_stringifyWalk(ctx, name, holder)
					if exists {
						object[name] = value
					}
				}
			} else {
				// Go maps are without order, so this doesn't conform to the ECMA ordering
				// standard, but oh well...
				holder.enumerate(false, func(name string) bool {
					value, exists := builtinJSON_stringifyWalk(ctx, name, holder)
					if exists {
						object[name] = value
					}
					return true
				})
			}
			return object, true
		}
	}
	return nil, false
}
<|endoftext|>


<commit_before>// package name: riffle
package main

import (
	"C"
	"encoding/json"
	"fmt"
	"unsafe"

	"github.com/exis-io/core"
	"github.com/exis-io/core/goRiffle"
)

/*
This is the lowest level core, just exposes the C API. Used for python, swift-linux, and osx.

You are responsible for cleaning up C references!

Every function here is reactive: it returns two indicies to callbacks to be triggered later.

Reg, Sub, Pub, Call all return indicies to callbacks they will later call.
*/

// Required main method
func main() {}

type mantle struct {
	app    core.App
	conn   *goRiffle.WebsocketConnection
	recv   chan []byte
	fabric string
}

var man = &mantle{
	recv:   make(chan []byte),
	fabric: core.ProudctionFabric,
}

//export NewDomain
func NewDomain(name *C.char) unsafe.Pointer {
	if man.app == nil {
		man.app = core.NewApp()
	}

	d := man.app.NewDomain(C.GoString(name), man)
	return unsafe.Pointer(&d)
}

//export Subscribe
func Subscribe(pdomain unsafe.Pointer, endpoint *C.char, data []bytes) []byte {
	d := *(*core.Domain)(pdomain)
	return coreInvoke(d.Subscribe, endpoint, unmarshall(data))
}

//export Register
func Register(pdomain unsafe.Pointer, endpoint *C.char, data []byte) []byte {
	d := *(*core.Domain)(pdomain)
	return coreInvoke(d.Register, endpoint, unmarshall(data))
}

//export Publish
func Publish(pdomain unsafe.Pointer, endpoint *C.char, data []byte) []byte {
	d := *(*core.Domain)(pdomain)
	return coreInvoke(d.Publish, endpoint, unmarshall(data))
}

//export Call
func Call(pdomain unsafe.Pointer, endpoint *C.char, data []byte) []byte {
	d := *(*core.Domain)(pdomain)
	return coreInvoke(d.Call, endpoint, unmarshall(data))
}

// Accepts a domain operator function, a list of any arguments, and an endpoint. Performs the operation on the given domain.
func coreInvoke(operation func(string, uint, []interface{}) error, endpoint *C.char, args []interface{}) []byte {
	cb, eb := core.NewID(), core.NewID()
	go func() {
		if err := operation(C.GoString(endpoint), cb, args); err != nil {
			man.InvokeError(eb, err.Error())
		}
	}()
	return marshall([]uint{cb, eb})
}

//export Yield
func Yield(args []byte) {
	// What to pass in as the id?

	// This needs work
	// core.Yield(C.GoString(e))
}

//export Unsubscribe
func Unsubscribe(pdomain unsafe.Pointer, e *C.char) {
	d := *(*core.Domain)(pdomain)
	d.Unsubscribe(C.GoString(e))
}

//export Unregister
func Unregister(pdomain unsafe.Pointer, e *C.char) {
	d := *(*core.Domain)(pdomain)
	d.Unregister(C.GoString(e))
}

//export Join
func Join(pdomain unsafe.Pointer) []byte {
	d := *(*core.Domain)(pdomain)
	cb, eb := core.NewID(), core.NewID()

	go func() {
		if man.conn != nil {
			man.InvokeError(eb, "Connection is already open!")
		}

		if c, err := goRiffle.Open(man.fabric); err != nil {
			man.InvokeError(eb, err.Error())
		} else {
			man.conn = c
			c.App = man.app

			if err := d.Join(c); err != nil {
				core.Warn("Unable to join! %s", err)
				man.InvokeError(eb, err.Error())
			} else {
				core.Info("Joined!")
				man.Invoke(cb, nil)
			}
		}
	}()

	return marshall([]uint{cb, eb})
}

//export Leave
func Leave(pdomain unsafe.Pointer) {
	d := *(*core.Domain)(pdomain)
	d.Leave()
}

//export Recieve
func Recieve() []byte {
	data := <-man.recv
	return data
}

func marshall(data interface{}) []byte {
	if r, e := json.Marshal(data); e == nil {
		return r
	} else {
		fmt.Println("Unable to marshall data!")
		return nil
	}
}

func unmarshall(data []byte) []interface{} {
	var ret []interface{}
	if err := json.Unmarshal(data, &ret); err != nil {
		// Handle this error a little more gracefully, eh?
		core.Warn("Unable to unmarshall call from crust! %s", data)
		return nil
	} else {
		return ret
	}
}

// Unexported Functions
func (m mantle) Invoke(id uint, args []interface{}) {
	core.Debug("Invoke called: ", id, args)
	// man.recv <- marshall(map[string]interface{}{"0": id, "1": args})
	man.recv <- marshall([]interface{}{id, args})
}

func (m mantle) InvokeError(id uint, e string) {
	// core.Debug("Invoking error: ", id, e)
	s := fmt.Sprintf("Err: %s", e)
	man.recv <- marshall([]interface{}{id, s})
}

func (m mantle) OnJoin(string) {
	fmt.Println("Domain joined!")
}

func (m mantle) OnLeave(string) {
	fmt.Println("Domain left!")
}

//export SetLoggingLevel
func SetLoggingLevel(l int) {
	core.LogLevel = l
}

//export SetLogLevelErr
func SetLogLevelErr() {
	core.LogLevel = core.LogLevelErr
}

//export SetLogLevelWarn
func SetLogLevelWarn() {
	core.LogLevel = core.LogLevelWarn
}

//export SetLogLevelInfo
func SetLogLevelInfo() {
	core.LogLevel = core.LogLevelInfo
}

//export SetLogLevelDebug
func SetLogLevelDebug() {
	core.LogLevel = core.LogLevelDebug
}

//export SetDevFabric
func SetDevFabric() {
	man.fabric = core.DevFabric
}

//export SetSandboxFabric
func SetSandboxFabric() {
	man.fabric = core.SandboxFabric
}

//export SetProductionFabric
func SetProductionFabric() {
	man.fabric = core.ProudctionFabric
}

//export SetLocalFabric
func SetLocalFabric() {
	man.fabric = core.ProudctionFabric
}

//export SetCustomFabric
func SetCustomFabric(url *C.char) {
	man.fabric = C.GoString(url)
}
<commit_msg>moving to ubuntu<commit_after>// package name: riffle
package main

import (
	"C"
	"encoding/json"
	"fmt"
	"unsafe"

	"github.com/exis-io/core"
	"github.com/exis-io/core/goRiffle"
)

/*
This is the lowest level core, just exposes the C API. Used for python, swift-linux, and osx.

You are responsible for cleaning up C references!

Every function here is reactive: it returns two indicies to callbacks to be triggered later.

Reg, Sub, Pub, Call all return indicies to callbacks they will later call.
*/

// Required main method
func main() {}

// By default always connect to the production fabric at node.exis.io
var fabric string = core.FabricProduction

//export NewDomain
func NewDomain(name *C.char) unsafe.Pointer  {
    d := core.NewDomain(C.GoString(name), nil)
    return unsafe.Pointer(&d)
}

//export Subdomain
func Subdomain(pdomain unsafe.Pointer, name *C.char) unsafe.Pointer  {
    d := *(*core.Domain)(pdomain)
    n := d.Subdomain(C.GoString(name))
    return unsafe.Pointer(&n)
}

// //export Subscribe
// func Subscribe(pdomain unsafe.Pointer, endpoint *C.char, data []bytes) []byte {
// 	d := *(*core.Domain)(pdomain)
// 	return coreInvoke(d.Subscribe, endpoint, unmarshall(data))
// }

// //export Register
// func Register(pdomain unsafe.Pointer, endpoint *C.char, data []byte) []byte {
// 	d := *(*core.Domain)(pdomain)
// 	return coreInvoke(d.Register, endpoint, unmarshall(data))
// }

// //export Publish
// func Publish(pdomain unsafe.Pointer, endpoint *C.char, data []byte) []byte {
// 	d := *(*core.Domain)(pdomain)
// 	return coreInvoke(d.Publish, endpoint, unmarshall(data))
// }

// //export Call
// func Call(pdomain unsafe.Pointer, endpoint *C.char, data []byte) []byte {
// 	d := *(*core.Domain)(pdomain)
// 	return coreInvoke(d.Call, endpoint, unmarshall(data))
// }

// // Accepts a domain operator function, a list of any arguments, and an endpoint. Performs the operation on the given domain.
// func coreInvoke(operation func(string, uint, []interface{}) error, endpoint *C.char, args []interface{}) []byte {
// 	cb, eb := core.NewID(), core.NewID()
// 	go func() {
// 		if err := operation(C.GoString(endpoint), cb, args); err != nil {
// 			man.InvokeError(eb, err.Error())
// 		}
// 	}()
// 	return marshall([]uint{cb, eb})
// }

// //export Yield
// func Yield(args []byte) {
// 	// What to pass in as the id?

// 	// This needs work
// 	// core.Yield(C.GoString(e))
// }

// //export Unsubscribe
// func Unsubscribe(pdomain unsafe.Pointer, e *C.char) {
// 	d := *(*core.Domain)(pdomain)
// 	d.Unsubscribe(C.GoString(e))
// }

// //export Unregister
// func Unregister(pdomain unsafe.Pointer, e *C.char) {
// 	d := *(*core.Domain)(pdomain)
// 	d.Unregister(C.GoString(e))
// }

// //export Join
// func Join(pdomain unsafe.Pointer) []byte {
// 	d := *(*core.Domain)(pdomain)
// 	cb, eb := core.NewID(), core.NewID()

// 	go func() {
// 		if man.conn != nil {
// 			man.InvokeError(eb, "Connection is already open!")
// 		}

// 		if c, err := goRiffle.Open(man.fabric); err != nil {
// 			man.InvokeError(eb, err.Error())
// 		} else {
// 			man.conn = c
// 			c.App = man.app

// 			if err := d.Join(c); err != nil {
// 				core.Warn("Unable to join! %s", err)
// 				man.InvokeError(eb, err.Error())
// 			} else {
// 				core.Info("Joined!")
// 				man.Invoke(cb, nil)
// 			}
// 		}
// 	}()

// 	return marshall([]uint{cb, eb})
// }

// //export Leave
// func Leave(pdomain unsafe.Pointer) {
// 	d := *(*core.Domain)(pdomain)
// 	d.Leave()
// }

// //export Recieve
// func Recieve() []byte {
// 	data := <-man.recv
// 	return data
// }

// func marshall(data interface{}) []byte {
// 	if r, e := json.Marshal(data); e == nil {
// 		return r
// 	} else {
// 		fmt.Println("Unable to marshall data!")
// 		return nil
// 	}
// }

// func unmarshall(data []byte) []interface{} {
// 	var ret []interface{}
// 	if err := json.Unmarshal(data, &ret); err != nil {
// 		// Handle this error a little more gracefully, eh?
// 		core.Warn("Unable to unmarshall call from crust! %s", data)
// 		return nil
// 	} else {
// 		return ret
// 	}
// }

// // Unexported Functions
// func (m mantle) Invoke(id uint, args []interface{}) {
// 	core.Debug("Invoke called: ", id, args)
// 	// man.recv <- marshall(map[string]interface{}{"0": id, "1": args})
// 	man.recv <- marshall([]interface{}{id, args})
// }

// func (m mantle) InvokeError(id uint, e string) {
// 	// core.Debug("Invoking error: ", id, e)
// 	s := fmt.Sprintf("Err: %s", e)
// 	man.recv <- marshall([]interface{}{id, s})
// }

// func (m mantle) OnJoin(string) {
// 	fmt.Println("Domain joined!")
// }

// func (m mantle) OnLeave(string) {
// 	fmt.Println("Domain left!")
// }

//export SetLogLevelOff
func SetLogLevelOff()   { core.LogLevel = core.LogLevelOff }
//export SetLogLevelApp
func SetLogLevelApp()   { core.LogLevel = core.LogLevelApp }
//export SetLogLevelErr
func SetLogLevelErr()   { core.LogLevel = core.LogLevelErr }
//export SetLogLevelWarn
func SetLogLevelWarn()  { core.LogLevel = core.LogLevelWarn }
//export SetLogLevelInfo
func SetLogLevelInfo()  { core.LogLevel = core.LogLevelInfo }
//export SetLogLevelDebug
func SetLogLevelDebug() { core.LogLevel = core.LogLevelDebug }

//export SetFabricDev
func SetFabricDev()        { fabric = core.FabricDev }
//export SetFabricSandbox
func SetFabricSandbox()    { fabric = core.FabricSandbox }
//export SetFabricProduction
func SetFabricProduction() { fabric = core.FabricProduction }
//export SetFabricLocal
func SetFabricLocal()      { fabric = core.FabricLocal }
//export SetFabric
func SetFabric(url string) { fabric = url }

//export Application
func Application(s string) { core.Application("%s", s) }
//export Debug
func Debug(s string)       { core.Debug("%s", s) }
//export Info
func Info(s string)        { core.Info("%s", s) }
//export Warn
func Warn(s string)        { core.Warn("%s", s) }
//export Error
func Error(s string)       { core.Error("%s", s) }
<|endoftext|>


<commit_before>package main

import (
	"github.com/minotar/minecraft"
)

const (
	// Get the skin size in bytes. Skins have an 8-bit channel depth and are
	// 64x32 pixels. Maximum of 8*64*32 plus 13 bytes for png metadata. They'll
	// rarely (never) be that large due to compression, but we'll leave some
	// extra wiggle to account for map overhead.
	SKIN_SIZE = 8 * (64 * 32)

	// Define a 64 MB cache size.
	CACHE_SIZE = 2 << 25

	// Based off those, calculate the maximum number of skins we'll store
	// in memory.
	SKIN_NUMBER = CACHE_SIZE / SKIN_SIZE
)

// Cache object that stores skins in memory.
type CacheMemory struct {
	// Map of usernames to minecraft skins. Lookups here are O(1), so that
	// makes my happy.
	Skins map[string]minecraft.Skin
	// Additionally keep a *slice* of usernames which we can update
	Usernames []string
}

// Find the position of a string in a slice. Returns -1 on failure.
func indexOf(str string, list []string) int {
	for index, value := range list {
		if value == str {
			return index
		}
	}

	return -1
}

func (c *CacheMemory) setup() {
	c.Skins = map[string]minecraft.Skin{}
	c.Usernames = []string{}

	log.Info("Loaded Memory cache")
}

// Returns whether the item exists in the cache.
func (c *CacheMemory) has(username string) bool {
	if _, exists := c.Skins[username]; exists {
		return true
	} else {
		return false
	}
}

// Retrieves the item from the cache. We'll promote it to the "top" of the
// cache, effectively updating its expiry time.
func (c *CacheMemory) pull(username string) minecraft.Skin {
	index := indexOf(username, c.Usernames)
	c.Usernames = append(c.Usernames, username)
	c.Usernames = append(c.Usernames[:index], c.Usernames[index+1:]...)

	return c.Skins[username]
}

// Adds the skin to the cache, remove the oldest, expired skin if the cache
// list is full.
func (c *CacheMemory) add(username string, skin minecraft.Skin) {
	if len(c.Usernames) >= SKIN_NUMBER {
		first := c.Usernames[0]
		delete(c.Skins, first)
		c.Usernames = append(c.Usernames[1:], username)
	} else {
		c.Usernames = append(c.Usernames, username)
	}

	c.Skins[username] = skin
}

// The byte size of the cache. Fairly rough... don't really want to venture
// into the land of manual memory management, because there be dragons.
func (c *CacheMemory) memory() uint64 {
	return uint64(len(c.Usernames) * SKIN_SIZE)
}
<commit_msg>More accurate skin size<commit_after>package main

import (
	"github.com/minotar/minecraft"
)

const (
	// Get the skin size in bytes. Stored as a []uint8, one byte each,
	// plus bounces. So 64 * 64 bytes and we'll throw in an extra 16
	// bytes of overhead.
	SKIN_SIZE = (64 * 64) + 16

	// Define a 64 MB cache size.
	CACHE_SIZE = 2 << 25

	// Based off those, calculate the maximum number of skins we'll store
	// in memory.
	SKIN_NUMBER = CACHE_SIZE / SKIN_SIZE
)

// Cache object that stores skins in memory.
type CacheMemory struct {
	// Map of usernames to minecraft skins. Lookups here are O(1), so that
	// makes my happy.
	Skins map[string]minecraft.Skin
	// Additionally keep a *slice* of usernames which we can update
	Usernames []string
}

// Find the position of a string in a slice. Returns -1 on failure.
func indexOf(str string, list []string) int {
	for index, value := range list {
		if value == str {
			return index
		}
	}

	return -1
}

func (c *CacheMemory) setup() {
	c.Skins = map[string]minecraft.Skin{}
	c.Usernames = []string{}

	log.Info("Loaded Memory cache")
}

// Returns whether the item exists in the cache.
func (c *CacheMemory) has(username string) bool {
	if _, exists := c.Skins[username]; exists {
		return true
	} else {
		return false
	}
}

// Retrieves the item from the cache. We'll promote it to the "top" of the
// cache, effectively updating its expiry time.
func (c *CacheMemory) pull(username string) minecraft.Skin {
	index := indexOf(username, c.Usernames)
	c.Usernames = append(c.Usernames, username)
	c.Usernames = append(c.Usernames[:index], c.Usernames[index+1:]...)

	return c.Skins[username]
}

// Adds the skin to the cache, remove the oldest, expired skin if the cache
// list is full.
func (c *CacheMemory) add(username string, skin minecraft.Skin) {
	if len(c.Usernames) >= SKIN_NUMBER {
		first := c.Usernames[0]
		delete(c.Skins, first)
		c.Usernames = append(c.Usernames[1:], username)
	} else {
		c.Usernames = append(c.Usernames, username)
	}

	c.Skins[username] = skin
}

// The byte size of the cache. Fairly rough... don't really want to venture
// into the land of manual memory management, because there be dragons.
func (c *CacheMemory) memory() uint64 {
	return uint64(len(c.Usernames) * SKIN_SIZE)
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Fabio Cagliero
// Use of this source code is governed by a MIT license found in the LICENSE file.

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

var pwd, buildDir, binDir, caddySourceDir string
var goos, goarch string
var plugins pluginsArray
var dev bool

// var avaiblePlugins bool = false

func init() {
	flag.StringVar(&goos, "goos", "", "OS for which to build")
	flag.StringVar(&goarch, "goarch", "", "ARCH for which to build")
	flag.Var(&plugins, "plugin", "Plugin to integrate in the build")
	flag.BoolVar(&dev, "dev", false, "Build the current master branch")
	// TODO
	//flag.BoolVar(&avaiblePlugins, "listplugins", false, "Display all the available plugins")
}

func main() {
	flag.Parse()

	var err error
	// Check if git is installed
	cmd := exec.Command("git", "--version")
	err = cmd.Run()
	check(err)

	// Getting current work directory
	pwd, err = os.Getwd()
	check(err)

	buildDir = pwd + "/build"
	binDir = pwd + "/bin"
	caddySourceDir = buildDir + "/src/github.com/mholt/caddy"

	os.Mkdir(buildDir, 0755)
	os.Mkdir(binDir, 0755)

	os.Setenv("GOPATH", buildDir)

	fmt.Println("Downloading caddy source code...")
	cmd = exec.Command("go", "get", "github.com/mholt/caddy/caddy")
	err = cmd.Run()
	check(err)

	cmd = exec.Command("go", "get", "github.com/caddyserver/builds")
	err = cmd.Run()
	check(err)

	// Git checkout to last tagged version
	// Skip for building the current master branch
	if !dev {
		cmd = exec.Command("git", "describe", "--abbrev=0", "--tags")
		cmd.Dir = caddySourceDir
		tag, err := cmd.Output()
		check(err)

		caddyVersion := strings.TrimSpace(string(tag))

		cmd = exec.Command("git", "checkout", caddyVersion)
		cmd.Dir = caddySourceDir
		err = cmd.Run()
		check(err)

		fmt.Println("Tag to build: ", caddyVersion)
	} else {
		fmt.Println("Branch to build: master")
	}

	pluginRepos := caddyAvailablePlugins()

	var selectedPlugins []string

	for _, plugin := range plugins {
		if pluginRepos[plugin] == "" {
			fmt.Printf("Plugin %s not found. Run with option -listplugins to see available plugins.\n", plugin)
			os.Exit(0)
		}

		selectedPlugins = append(selectedPlugins, pluginRepos[plugin])
	}

	if len(selectedPlugins) > 0 {
		addPlugins(selectedPlugins)
	}

	fmt.Println("Building...")

	cmd = exec.Command("go", "run", "build.go", "-goos", goos, "-goarch", goarch)
	cmd.Dir = caddySourceDir + "/caddy"
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stdout
	err = cmd.Run()
	check(err)

	err = os.Rename(caddySourceDir+"/caddy/caddy", binDir+"/caddy")

	fmt.Println("Removing build dir...")
	os.RemoveAll(buildDir)

	fmt.Println("Done! Your caddy executable is in ", binDir)
}

// Define plugin type
type pluginsArray []string

func (p *pluginsArray) String() string {
	return fmt.Sprintf("%d", *p)
}

func (p *pluginsArray) Set(plugin string) error {
	*p = append(*p, plugin)
	return nil
}

// Other functions
func check(e error) {
	if e != nil {
		panic(e)
	}
}

func caddyAvailablePlugins() map[string]string {
	file, err := os.Open(caddySourceDir + "/caddyhttp/httpserver/plugin.go")
	check(err)

	var varDirectives bool = false
	scanner := bufio.NewScanner(file)

	pluginRepos := make(map[string]string)

	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())

		if line == "// The ordering of this list is important." {
			varDirectives = true
		}

		if varDirectives && line == "}" {
			varDirectives = false
			break
		}

		if varDirectives {
			pluginRegExp := regexp.MustCompile(`^\"([a-zA-Z_-]+)\",\s+\/\/\s([a-zA-Z_\-.\/]+)$`)
			check(err)

			if pluginRegExp.MatchString(line) {
				subMatches := (pluginRegExp.FindStringSubmatch(line))[1:]
				pluginRepos[subMatches[0]] = subMatches[1]
			}
		}
	}

	return pluginRepos
}

func addPlugins(selectedPlugins []string) {
	for k, plugin := range selectedPlugins {
		fmt.Printf("Downloading %s plugin source code...\n", plugin)
		cmd := exec.Command("go", "get", plugin)
		err := cmd.Run()
		check(err)

		selectedPlugins[k] = fmt.Sprintf("\t_ \"%s\"", plugin)
	}

	fileRunGo, err := ioutil.ReadFile(caddySourceDir + "/caddy/caddymain/run.go")
	check(err)

	lines := strings.Split(string(fileRunGo), "\n")

	for i, line := range lines {
		if strings.TrimSpace(line) == "// This is where other plugins get plugged in (imported)" {
			lines = append(lines, selectedPlugins...)
			copy(lines[i+1+len(selectedPlugins):], lines[i+1:])
			copy(lines[i+1:], selectedPlugins)
			break
		}
	}

	output := strings.Join(lines, "\n")
	err = ioutil.WriteFile(caddySourceDir+"/caddy/caddymain/run.go", []byte(output), 0644)
	check(err)
}
<commit_msg>Nicer way to detect if a key is not present in a map<commit_after>// Copyright (c) 2017 Fabio Cagliero
// Use of this source code is governed by a MIT license found in the LICENSE file.

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

var pwd, buildDir, binDir, caddySourceDir string
var goos, goarch string
var plugins pluginsArray
var dev bool

// var avaiblePlugins bool = false

func init() {
	flag.StringVar(&goos, "goos", "", "OS for which to build")
	flag.StringVar(&goarch, "goarch", "", "ARCH for which to build")
	flag.Var(&plugins, "plugin", "Plugin to integrate in the build")
	flag.BoolVar(&dev, "dev", false, "Build the current master branch")
	// TODO
	//flag.BoolVar(&avaiblePlugins, "listplugins", false, "Display all the available plugins")
}

func main() {
	flag.Parse()

	var err error
	// Check if git is installed
	cmd := exec.Command("git", "--version")
	err = cmd.Run()
	check(err)

	// Getting current work directory
	pwd, err = os.Getwd()
	check(err)

	buildDir = pwd + "/build"
	binDir = pwd + "/bin"
	caddySourceDir = buildDir + "/src/github.com/mholt/caddy"

	os.Mkdir(buildDir, 0755)
	os.Mkdir(binDir, 0755)

	os.Setenv("GOPATH", buildDir)

	fmt.Println("Downloading caddy source code...")
	cmd = exec.Command("go", "get", "github.com/mholt/caddy/caddy")
	err = cmd.Run()
	check(err)

	cmd = exec.Command("go", "get", "github.com/caddyserver/builds")
	err = cmd.Run()
	check(err)

	// Git checkout to last tagged version
	// Skip for building the current master branch
	if !dev {
		cmd = exec.Command("git", "describe", "--abbrev=0", "--tags")
		cmd.Dir = caddySourceDir
		tag, err := cmd.Output()
		check(err)

		caddyVersion := strings.TrimSpace(string(tag))

		cmd = exec.Command("git", "checkout", caddyVersion)
		cmd.Dir = caddySourceDir
		err = cmd.Run()
		check(err)

		fmt.Println("Tag to build: ", caddyVersion)
	} else {
		fmt.Println("Branch to build: master")
	}

	pluginRepos := caddyAvailablePlugins()

	var selectedPlugins []string

	for _, plugin := range plugins {
		if _, found := pluginRepos[plugin]; !found {
			// TODO
			// fmt.Printf("Plugin %s not found. Run with option -listplugins to see available plugins.\n", plugin)
			fmt.Printf("Plugin %s not found.\n", plugin)
			os.Exit(0)
		}

		selectedPlugins = append(selectedPlugins, pluginRepos[plugin])
	}

	if len(selectedPlugins) > 0 {
		addPlugins(selectedPlugins)
	}

	fmt.Println("Building...")

	cmd = exec.Command("go", "run", "build.go", "-goos", goos, "-goarch", goarch)
	cmd.Dir = caddySourceDir + "/caddy"
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stdout
	err = cmd.Run()
	check(err)

	err = os.Rename(caddySourceDir+"/caddy/caddy", binDir+"/caddy")

	fmt.Println("Removing build dir...")
	os.RemoveAll(buildDir)

	fmt.Println("Done! Your caddy executable is in ", binDir)
}

// Define plugin type
type pluginsArray []string

func (p *pluginsArray) String() string {
	return fmt.Sprintf("%d", *p)
}

func (p *pluginsArray) Set(plugin string) error {
	*p = append(*p, plugin)
	return nil
}

// Other functions
func check(e error) {
	if e != nil {
		panic(e)
	}
}

func caddyAvailablePlugins() map[string]string {
	file, err := os.Open(caddySourceDir + "/caddyhttp/httpserver/plugin.go")
	check(err)

	var varDirectives bool = false
	scanner := bufio.NewScanner(file)

	pluginRepos := make(map[string]string)

	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())

		if line == "// The ordering of this list is important." {
			varDirectives = true
		}

		if varDirectives && line == "}" {
			varDirectives = false
			break
		}

		if varDirectives {
			pluginRegExp := regexp.MustCompile(`^\"([a-zA-Z_-]+)\",\s+\/\/\s([a-zA-Z_\-.\/]+)$`)
			check(err)

			if pluginRegExp.MatchString(line) {
				subMatches := (pluginRegExp.FindStringSubmatch(line))[1:]
				pluginRepos[subMatches[0]] = subMatches[1]
			}
		}
	}

	return pluginRepos
}

func addPlugins(selectedPlugins []string) {
	for k, plugin := range selectedPlugins {
		fmt.Printf("Downloading %s plugin source code...\n", plugin)
		cmd := exec.Command("go", "get", plugin)
		err := cmd.Run()
		check(err)

		selectedPlugins[k] = fmt.Sprintf("\t_ \"%s\"", plugin)
	}

	fileRunGo, err := ioutil.ReadFile(caddySourceDir + "/caddy/caddymain/run.go")
	check(err)

	lines := strings.Split(string(fileRunGo), "\n")

	for i, line := range lines {
		if strings.TrimSpace(line) == "// This is where other plugins get plugged in (imported)" {
			lines = append(lines, selectedPlugins...)
			copy(lines[i+1+len(selectedPlugins):], lines[i+1:])
			copy(lines[i+1:], selectedPlugins)
			break
		}
	}

	output := strings.Join(lines, "\n")
	err = ioutil.WriteFile(caddySourceDir+"/caddy/caddymain/run.go", []byte(output), 0644)
	check(err)
}
<|endoftext|>


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

package session

import ()

// バックエンドのデータもこのプログラム専用の前提。

// セッションの格納庫。
type Db interface {
	// 取得。
	Get(id string) (*Element, error)

	// 保存。
	Save(elem *Element) error
}
<commit_msg>セッション DB への保存時に保存期限を指定するように変更<commit_after>// Copyright 2015 realglobe, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package session

import (
	"time"
)

// バックエンドのデータもこのプログラム専用の前提。

// セッションの格納庫。
type Db interface {
	// 取得。
	Get(id string) (*Element, error)

	// 保存。
	// exp: 保存期限。この期間以降は Get できなくて良い。
	Save(elem *Element, exp time.Time) error
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package versionabledb

import (
	"sync"

	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/versiondb"
	"github.com/ava-labs/avalanchego/utils/wrappers"
)

type Database struct {
	lock sync.RWMutex

	versionEnabled bool
	db             database.Database
	vdb            *versiondb.Database
}

// New returns a new prefixed database
func New(db database.Database) *Database {
	return &Database{
		db:  db,
		vdb: versiondb.New(db),
	}
}

// Has implements the database.Database interface
func (db *Database) Has(key []byte) (bool, error) {
	db.lock.RLock()
	defer db.lock.RUnlock()

	if db.versionEnabled {
		return db.vdb.Has(key)
	}
	return db.db.Has(key)
}

// Get implements the database.Database interface
func (db *Database) Get(key []byte) ([]byte, error) {
	db.lock.RLock()
	defer db.lock.RUnlock()

	if db.versionEnabled {
		return db.vdb.Get(key)
	}
	return db.db.Get(key)
}

// Put implements the database.Database interface
func (db *Database) Put(key, value []byte) error {
	db.lock.Lock()
	defer db.lock.Unlock()

	if db.versionEnabled {
		return db.vdb.Put(key, value)
	}
	return db.db.Put(key, value)
}

// Delete implements the database.Database interface
func (db *Database) Delete(key []byte) error {
	db.lock.Lock()
	defer db.lock.Unlock()

	if db.versionEnabled {
		return db.vdb.Delete(key)
	}
	return db.db.Delete(key)
}

// NewBatch implements the database.Database interface
func (db *Database) NewBatch() database.Batch {
	return &batch{
		db:    db,
		Batch: db.db.NewBatch(),
	}
}

// NewIterator implements the database.Database interface
func (db *Database) NewIterator() database.Iterator {
	return db.NewIteratorWithStartAndPrefix(nil, nil)
}

// NewIteratorWithStart implements the database.Database interface
func (db *Database) NewIteratorWithStart(start []byte) database.Iterator {
	return db.NewIteratorWithStartAndPrefix(start, nil)
}

// NewIteratorWithPrefix implements the database.Database interface
func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator {
	return db.NewIteratorWithStartAndPrefix(nil, prefix)
}

// NewIteratorWithStartAndPrefix implements the database.Database interface
func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
	db.lock.RLock()
	defer db.lock.RUnlock()

	if db.versionEnabled {
		return db.vdb.NewIteratorWithStartAndPrefix(start, prefix)
	}
	return db.db.NewIteratorWithStartAndPrefix(start, prefix)
}

// Stat implements the database.Database interface
func (db *Database) Stat(stat string) (string, error) {
	db.lock.RLock()
	defer db.lock.RUnlock()

	// Note: versiondb passes through to the underlying db, so we skip
	// checking the [versionEnabled] flag here.
	return db.db.Stat(stat)
}

// Compact implements the database.Database interface
func (db *Database) Compact(start, limit []byte) error {
	db.lock.Lock()
	defer db.lock.Unlock()

	// Note: versiondb passes through to the underlying db, so we skip
	// checking the [versionEnabled] flag here.
	return db.db.Compact(start, limit)
}

// StartCommit sets the [versionEnabled] flag to true, so that
// all operations are performed on top of the versiondb instead
// of the underlying database.
func (db *Database) StartCommit() {
	db.lock.Lock()
	defer db.lock.Unlock()

	db.versionEnabled = true
}

// EndCommit sets the [versionEnabled] flag back to false and calls
// Abort() on the versiondb.
func (db *Database) AbortCommit() {
	db.lock.Lock()
	defer db.lock.Unlock()

	db.versionEnabled = false
	db.vdb.Abort()
}

// EndCommit sets the [versionEnabled] flag back to false and calls
// Abort() on the versiondb.
func (db *Database) EndCommit() {
	db.versionEnabled = false
	db.vdb.Abort()
	db.lock.Unlock()
}

// CommitBatch returns a batch that contains all uncommitted puts/deletes.
// Calling Write() on the returned batch causes the puts/deletes to be
// written to the underlying database. CommitBatch holds onto the lock,
// blocking all other database operations until EndCommit() is called.
func (db *Database) CommitBatch() (database.Batch, error) {
	db.lock.Lock()

	return db.vdb.CommitBatch()
}

// Close implements the database.Database interface
func (db *Database) Close() error {
	db.lock.Lock()
	defer db.lock.Unlock()

	errs := wrappers.Errs{}
	errs.Add(
		db.vdb.Close(),
		db.db.Close(),
	)
	return errs.Err
}

type batch struct {
	db *Database
	database.Batch
}

// Write implements the Database interface
func (b *batch) Write() error {
	b.db.lock.Lock()
	defer b.db.lock.Unlock()

	if b.db.versionEnabled {
		return b.Batch.Replay(b.db.vdb)
	}

	return b.Batch.Write()
}

// Inner returns itself
func (b *batch) Inner() database.Batch { return b.Batch }
<commit_msg>Add commit to versionabledb interface<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package versionabledb

import (
	"sync"

	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/versiondb"
	"github.com/ava-labs/avalanchego/utils/wrappers"
)

type Database struct {
	lock sync.RWMutex

	versionEnabled bool
	db             database.Database
	vdb            *versiondb.Database
}

// New returns a new prefixed database
func New(db database.Database) *Database {
	return &Database{
		db:  db,
		vdb: versiondb.New(db),
	}
}

// Has implements the database.Database interface
func (db *Database) Has(key []byte) (bool, error) {
	db.lock.RLock()
	defer db.lock.RUnlock()

	if db.versionEnabled {
		return db.vdb.Has(key)
	}
	return db.db.Has(key)
}

// Get implements the database.Database interface
func (db *Database) Get(key []byte) ([]byte, error) {
	db.lock.RLock()
	defer db.lock.RUnlock()

	if db.versionEnabled {
		return db.vdb.Get(key)
	}
	return db.db.Get(key)
}

// Put implements the database.Database interface
func (db *Database) Put(key, value []byte) error {
	db.lock.Lock()
	defer db.lock.Unlock()

	if db.versionEnabled {
		return db.vdb.Put(key, value)
	}
	return db.db.Put(key, value)
}

// Delete implements the database.Database interface
func (db *Database) Delete(key []byte) error {
	db.lock.Lock()
	defer db.lock.Unlock()

	if db.versionEnabled {
		return db.vdb.Delete(key)
	}
	return db.db.Delete(key)
}

// NewBatch implements the database.Database interface
func (db *Database) NewBatch() database.Batch {
	return &batch{
		db:    db,
		Batch: db.db.NewBatch(),
	}
}

// NewIterator implements the database.Database interface
func (db *Database) NewIterator() database.Iterator {
	return db.NewIteratorWithStartAndPrefix(nil, nil)
}

// NewIteratorWithStart implements the database.Database interface
func (db *Database) NewIteratorWithStart(start []byte) database.Iterator {
	return db.NewIteratorWithStartAndPrefix(start, nil)
}

// NewIteratorWithPrefix implements the database.Database interface
func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator {
	return db.NewIteratorWithStartAndPrefix(nil, prefix)
}

// NewIteratorWithStartAndPrefix implements the database.Database interface
func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
	db.lock.RLock()
	defer db.lock.RUnlock()

	if db.versionEnabled {
		return db.vdb.NewIteratorWithStartAndPrefix(start, prefix)
	}
	return db.db.NewIteratorWithStartAndPrefix(start, prefix)
}

// Stat implements the database.Database interface
func (db *Database) Stat(stat string) (string, error) {
	db.lock.RLock()
	defer db.lock.RUnlock()

	// Note: versiondb passes through to the underlying db, so we skip
	// checking the [versionEnabled] flag here.
	return db.db.Stat(stat)
}

// Compact implements the database.Database interface
func (db *Database) Compact(start, limit []byte) error {
	db.lock.Lock()
	defer db.lock.Unlock()

	// Note: versiondb passes through to the underlying db, so we skip
	// checking the [versionEnabled] flag here.
	return db.db.Compact(start, limit)
}

// StartCommit sets the [versionEnabled] flag to true, so that
// all operations are performed on top of the versiondb instead
// of the underlying database.
func (db *Database) StartCommit() {
	db.lock.Lock()
	defer db.lock.Unlock()

	db.versionEnabled = true
}

// Commit writes all the operations in the versiondb to the
// underlying database and sets the [versionEnabled] flag to false.
// If StartCommit() was never called, then Commit() will be a no-op.
func (db *Database) Commit() error {
	db.lock.Lock()
	defer db.lock.Unlock()

	db.versionEnabled = false
	return db.vdb.Commit()
}

// EndCommit sets the [versionEnabled] flag back to false and calls
// Abort() on the versiondb.
func (db *Database) AbortCommit() {
	db.lock.Lock()
	defer db.lock.Unlock()

	db.versionEnabled = false
	db.vdb.Abort()
}

// EndCommit sets the [versionEnabled] flag back to false and calls
// Abort() on the versiondb.
func (db *Database) EndCommit() {
	db.versionEnabled = false
	db.vdb.Abort()
	db.lock.Unlock()
}

// CommitBatch returns a batch that contains all uncommitted puts/deletes.
// Calling Write() on the returned batch causes the puts/deletes to be
// written to the underlying database. CommitBatch holds onto the lock,
// blocking all other database operations until EndCommit() is called.
func (db *Database) CommitBatch() (database.Batch, error) {
	db.lock.Lock()

	return db.vdb.CommitBatch()
}

// Close implements the database.Database interface
func (db *Database) Close() error {
	db.lock.Lock()
	defer db.lock.Unlock()

	errs := wrappers.Errs{}
	errs.Add(
		db.vdb.Close(),
		db.db.Close(),
	)
	return errs.Err
}

type batch struct {
	db *Database
	database.Batch
}

// Write implements the Database interface
func (b *batch) Write() error {
	b.db.lock.Lock()
	defer b.db.lock.Unlock()

	if b.db.versionEnabled {
		return b.Batch.Replay(b.db.vdb)
	}

	return b.Batch.Write()
}

// Inner returns itself
func (b *batch) Inner() database.Batch { return b.Batch }
<|endoftext|>


<commit_before>package portforward

import (
	"fmt"
	"net"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
)

var (
	helloWorld = []byte("hello world!")
)

func TestPortForwardSuite(t *testing.T) {
	suite.Run(t, new(PortForwardSuite))
}

type PortForwardSuite struct {
	suite.Suite
	listenPort  string
	forwardPort string
	stop        chan struct{}
}

func (s *PortForwardSuite) SetupSuite() {
	s.listenPort = fmt.Sprintf(":%d", 1234)
	s.forwardPort = fmt.Sprintf(":%d", 5678)

	// Create a echo tcp server
	go echoTcpServer(s.forwardPort)
}

func (s *PortForwardSuite) TestReconnect() {
	// connection should fail because no one is listen to the port.
	s.writeAndCheck(false)

	// Now start port forwarding
	stop, err := PortForward(s.listenPort, s.forwardPort)
	assert.NoError(s.T(), err)

	// after port forwarding it should work
	s.writeAndCheck(true)
	s.writeAndCheck(true)

	// port forwarding stopped. should fail.
	stop <- struct{}{}
	time.Sleep(1 * time.Second)
	fmt.Println("stopped1")
	s.writeAndCheck(false)

	// port forwarding again. should success.
	stop, err = PortForward(s.listenPort, s.forwardPort)
	s.writeAndCheck(true)
	stop <- struct{}{}
	// time.Sleep(1 * time.Second)
	fmt.Println("stopped2")
}

func (s *PortForwardSuite) writeAndCheck(shouldSuccess bool) {
	conn, err := net.Dial("tcp", s.listenPort)
	if !shouldSuccess {
		assert.Error(s.T(), err)
		return
	}
	assert.NoError(s.T(), err)
	conn.Write(helloWorld)
	buf := make([]byte, 2014)
	l, err := conn.Read(buf)
	assert.NoError(s.T(), err)
	assert.Equal(s.T(), helloWorld, buf[:l])
	conn.Close()
}

func echoTcpServer(hostPort string) {
	l, err := net.Listen("tcp", hostPort)
	if err != nil {
		panic(fmt.Sprintf("Error listening:" + err.Error()))
	}

	// Close the listener when the application closes.
	defer l.Close()
	fmt.Println("Listening on " + hostPort)
	for {
		// Listen for an incoming connection.
		conn, err := l.Accept()
		if err != nil {
			panic(fmt.Sprintf("Error accepting: ", err.Error()))
		}

		// Make a buffer to hold incoming data.
		buf := make([]byte, 1024)
		// Read the incoming connection into the buffer.
		reqLen, err := conn.Read(buf)
		if err != nil {
			fmt.Println("Error reading:", err.Error())
		}
		conn.Write(buf[:reqLen])
		// Close the connection when you're done with it.
		conn.Close()
	}
}
<commit_msg>Fix code<commit_after>package portforward

import (
	"fmt"
	"net"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
)

var (
	helloWorld = []byte("hello world!")
)

func TestPortForwardSuite(t *testing.T) {
	suite.Run(t, new(PortForwardSuite))
}

type PortForwardSuite struct {
	suite.Suite
	listenPort  string
	forwardPort string
	stop        chan struct{}
}

func (s *PortForwardSuite) SetupSuite() {
	s.listenPort = fmt.Sprintf(":%d", 1234)
	s.forwardPort = fmt.Sprintf(":%d", 5678)

	// Create a echo tcp server
	go echoTcpServer(s.forwardPort)
}

func (s *PortForwardSuite) TestReconnect() {
	// connection should fail because no one is listen to the port.
	s.writeAndCheck(false)

	// Now start port forwarding
	stop, err := PortForward(s.listenPort, s.forwardPort)
	assert.NoError(s.T(), err)

	// after port forwarding it should work
	s.writeAndCheck(true)
	s.writeAndCheck(true)

	// port forwarding stopped. should fail.
	stop <- struct{}{}
	time.Sleep(1 * time.Second)
	fmt.Println("stopped1")
	s.writeAndCheck(false)

	// port forwarding again. should success.
	stop, err = PortForward(s.listenPort, s.forwardPort)
	s.writeAndCheck(true)
	stop <- struct{}{}
	// time.Sleep(1 * time.Second)
	fmt.Println("stopped2")
}

func (s *PortForwardSuite) writeAndCheck(shouldSuccess bool) {
	conn, err := net.Dial("tcp", s.listenPort)
	if !shouldSuccess {
		assert.Error(s.T(), err)
		return
	}
	assert.NoError(s.T(), err)
	conn.Write(helloWorld)
	buf := make([]byte, 2014)
	l, err := conn.Read(buf)
	assert.NoError(s.T(), err)
	assert.Equal(s.T(), helloWorld, buf[:l])
	conn.Close()
}

func echoTcpServer(hostPort string) {
	l, err := net.Listen("tcp", hostPort)
	if err != nil {
		panic(fmt.Sprintf("Error listening:" + err.Error()))
	}

	// Close the listener when the application closes.
	defer l.Close()
	fmt.Println("Listening on " + hostPort)
	for {
		// Listen for an incoming connection.
		conn, err := l.Accept()
		if err != nil {
			panic(fmt.Sprintf("Error accepting: %s", err.Error()))
		}

		// Make a buffer to hold incoming data.
		buf := make([]byte, 1024)
		// Read the incoming connection into the buffer.
		reqLen, err := conn.Read(buf)
		if err != nil {
			fmt.Println("Error reading:", err.Error())
		}
		conn.Write(buf[:reqLen])
		// Close the connection when you're done with it.
		conn.Close()
	}
}
<|endoftext|>